﻿#ifndef HCUSTOMPLOT_H
#define HCUSTOMPLOT_H


#include <QObject>
#include <QPointer>
#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QMouseEvent>
#include <QPixmap>
#include <QVector>
#include <QString>
#include <QDateTime>
#include <QMultiMap>
#include <QFlags>
#include <QDebug>
#include <QVector2D>
#include <QStack>
#include <QCache>
#include <QMargins>
#include <qmath.h>
#include <limits>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#  include <qnumeric.h>
#  include <QPrinter>
#  include <QPrintEngine>
#else
#  include <QtNumeric>
#  include <QtPrintSupport>
#endif

class HPainter;
class HPlot;
class HLayerable;
class HLayoutElement;
class HLayout;
class HAxis;
class HAxisRect;
class HAxisPainterPrivate;
class HAbstractPlottable;
class HGraph;
class HAbstractItem;
class HItemPosition;
class HLayer;
class HPlotTitle;
class HLegend;
class HAbstractLegendItem;
class HColorMap;
class HColorScale;



// decl definitions for shared library compilation/usage:
#if defined(HMPPLOT_COMPILE_LIBRARY)
#  define H_LIB_DECL Q_DECL_EXPORT
#else
#  define H_LIB_DECL Q_DECL_IMPORT
#endif


namespace HMP
{

enum MarginSide { msLeft     = 0x01 ///< <tt>0x01</tt> left margin
                  ,msRight   = 0x02 ///< <tt>0x02</tt> right margin
                  ,msTop     = 0x04 ///< <tt>0x04</tt> top margin
                  ,msBottom  = 0x08 ///< <tt>0x08</tt> bottom margin
                  ,msAll     = 0xFF ///< <tt>0xFF</tt> all margins
                  ,msNone    = 0x00 ///< <tt>0x00</tt> no margin
                };
Q_DECLARE_FLAGS(MarginSides, MarginSide)

enum AntialiasedElement { aeAxes           = 0x0001 ///< <tt>0x0001</tt> Axis base line and tick marks
                          ,aeGrid          = 0x0002 ///< <tt>0x0002</tt> Grid lines
                          ,aeSubGrid       = 0x0004 ///< <tt>0x0004</tt> Sub grid lines
                          ,aeLegend        = 0x0008 ///< <tt>0x0008</tt> Legend box
                          ,aeLegendItems   = 0x0010 ///< <tt>0x0010</tt> Legend items
                          ,aePlottables    = 0x0020 ///< <tt>0x0020</tt> Main lines of plottables (excluding error bars, see element \ref aeErrorBars)
                          ,aeItems         = 0x0040 ///< <tt>0x0040</tt> Main lines of items
                          ,aeScatters      = 0x0080 ///< <tt>0x0080</tt> Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)
                          ,aeErrorBars     = 0x0100 ///< <tt>0x0100</tt> Error bars
                          ,aeFills         = 0x0200 ///< <tt>0x0200</tt> Borders of fills (e.g. under or between graphs)
                          ,aeZeroLine      = 0x0400 ///< <tt>0x0400</tt> Zero-lines, see \ref HGrid::setZeroLinePen
                          ,aeAll           = 0xFFFF ///< <tt>0xFFFF</tt> All elements
                          ,aeNone          = 0x0000 ///< <tt>0x0000</tt> No elements
                        };
Q_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement)


enum PlottingHint { phNone            = 0x000 ///< <tt>0x000</tt> No hints are set
                    ,phFastPolylines  = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
                                              ///<                especially of the line segment joins. (Only relevant for solid line pens.)
                    ,phForceRepaint   = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when HPlot::replot() is called with parameter \ref HPlot::rpHint.
                                              ///<                This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
                    ,phCacheLabels    = 0x004 ///< <tt>0x004</tt> axis (tick) labels will be cached as pixmaps, increasing replot performance.
                  };
Q_DECLARE_FLAGS(PlottingHints, PlottingHint)

enum Interaction { iRangeDrag         = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \ref HAxisRect::setRangeDrag, \ref HAxisRect::setRangeDragAxes)
                   ,iRangeZoom        = 0x002 ///< <tt>0x002</tt> Axis ranges are zoomable with the mouse wheel (see \ref HAxisRect::setRangeZoom, \ref HAxisRect::setRangeZoomAxes)
                   ,iMultiSelect      = 0x004 ///< <tt>0x004</tt> The user can select multiple objects by holding the modifier set by \ref HPlot::setMultiSelectModifier while clicking
                   ,iSelectPlottables = 0x008 ///< <tt>0x008</tt> Plottables are selectable (e.g. graphs, curves, bars,... see HAbstractPlottable)
                   ,iSelectAxes       = 0x010 ///< <tt>0x010</tt> Axes are selectable (or parts of them, see HAxis::setSelectableParts)
                   ,iSelectLegend     = 0x020 ///< <tt>0x020</tt> Legends are selectable (or their child items, see HLegend::setSelectableParts)
                   ,iSelectItems      = 0x040 ///< <tt>0x040</tt> Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref HAbstractItem)
                   ,iSelectOther      = 0x080 ///< <tt>0x080</tt> All other objects are selectable (e.g. your own derived layerables, the plot title,...)
                 };
Q_DECLARE_FLAGS(Interactions, Interaction)

inline bool isInvalidData(double value)
{
  return qIsNaN(value) || qIsInf(value);
}

inline bool isInvalidData(double value1, double value2)
{
  return isInvalidData(value1) || isInvalidData(value2);
}

inline void setMarginValue(QMargins &margins, HMP::MarginSide side, int value)
{
  switch (side)
  {
    case HMP::msLeft: margins.setLeft(value); break;
    case HMP::msRight: margins.setRight(value); break;
    case HMP::msTop: margins.setTop(value); break;
    case HMP::msBottom: margins.setBottom(value); break;
    case HMP::msAll: margins = QMargins(value, value, value, value); break;
    default: break;
  }
}

inline int getMarginValue(const QMargins &margins, HMP::MarginSide side)
{
  switch (side)
  {
    case HMP::msLeft: return margins.left();
    case HMP::msRight: return margins.right();
    case HMP::msTop: return margins.top();
    case HMP::msBottom: return margins.bottom();
    default: break;
  }
  return 0;
}

} // end of namespace HMP

Q_DECLARE_OPERATORS_FOR_FLAGS(HMP::AntialiasedElements)
Q_DECLARE_OPERATORS_FOR_FLAGS(HMP::PlottingHints)
Q_DECLARE_OPERATORS_FOR_FLAGS(HMP::MarginSides)
Q_DECLARE_OPERATORS_FOR_FLAGS(HMP::Interactions)


class H_LIB_DECL HScatterStyle
{
  Q_GADGET
public:

  Q_ENUMS(ScatterShape)
  enum ScatterShape { ssNone       ///< no scatter symbols are drawn (e.g. in HGraph, data only represented with lines)
                      ,ssDot       ///< \enumimage{ssDot.png} a single pixel (use \ref ssDisc or \ref ssCircle if you want a round shape with a certain radius)
                      ,ssCross     ///< \enumimage{ssCross.png} a cross
                      ,ssPlus      ///< \enumimage{ssPlus.png} a plus
                      ,ssCircle    ///< \enumimage{ssCircle.png} a circle
                      ,ssDisc      ///< \enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle)
                      ,ssSquare    ///< \enumimage{ssSquare.png} a square
                      ,ssDiamond   ///< \enumimage{ssDiamond.png} a diamond
                      ,ssStar      ///< \enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
                      ,ssTriangle  ///< \enumimage{ssTriangle.png} an equilateral triangle, standing on baseline
                      ,ssTriangleInverted ///< \enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner
                      ,ssCrossSquare      ///< \enumimage{ssCrossSquare.png} a square with a cross inside
                      ,ssPlusSquare       ///< \enumimage{ssPlusSquare.png} a square with a plus inside
                      ,ssCrossCircle      ///< \enumimage{ssCrossCircle.png} a circle with a cross inside
                      ,ssPlusCircle       ///< \enumimage{ssPlusCircle.png} a circle with a plus inside
                      ,ssPeace     ///< \enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines
                      ,ssPixmap    ///< a custom pixmap specified by \ref setPixmap, centered on the data point coordinates
                      ,ssCustom    ///< custom painter operations are performed per scatter (As QPainterPath, see \ref setCustomPath)
                    };

  HScatterStyle();
  HScatterStyle(ScatterShape shape, double size=6);
  HScatterStyle(ScatterShape shape, const QColor &color, double size);
  HScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size);
  HScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size);
  HScatterStyle(const QPixmap &pixmap);
  HScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush=Qt::NoBrush, double size=6);

  // getters:
  double size() const { return mSize; }
  ScatterShape shape() const { return mShape; }
  QPen pen() const { return mPen; }
  QBrush brush() const { return mBrush; }
  QPixmap pixmap() const { return mPixmap; }
  QPainterPath customPath() const { return mCustomPath; }

  // setters:
  void setSize(double size);
  void setShape(ScatterShape shape);
  void setPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setPixmap(const QPixmap &pixmap);
  void setCustomPath(const QPainterPath &customPath);

  // non-property methods:
  bool isNone() const { return mShape == ssNone; }
  bool isPenDefined() const { return mPenDefined; }
  void applyTo(HPainter *painter, const QPen &defaultPen) const;
  void drawShape(HPainter *painter, QPointF pos) const;
  void drawShape(HPainter *painter, double x, double y) const;

protected:
  // property members:
  double mSize;
  ScatterShape mShape;
  QPen mPen;
  QBrush mBrush;
  QPixmap mPixmap;
  QPainterPath mCustomPath;

  // non-property members:
  bool mPenDefined;
};
Q_DECLARE_TYPEINFO(HScatterStyle, Q_MOVABLE_TYPE);


class H_LIB_DECL HPainter : public QPainter
{
  Q_GADGET
public:

  enum PainterMode { pmDefault       = 0x00   ///< <tt>0x00</tt> Default mode for painting on screen devices
                     ,pmVectorized   = 0x01   ///< <tt>0x01</tt> Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes.
                     ,pmNoCaching    = 0x02   ///< <tt>0x02</tt> Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels
                     ,pmNonCosmetic  = 0x04   ///< <tt>0x04</tt> Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.)
                   };
  Q_FLAGS(PainterMode PainterModes)
  Q_DECLARE_FLAGS(PainterModes, PainterMode)

  HPainter();
  HPainter(QPaintDevice *device);
  ~HPainter();

  // getters:
  bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); }
  PainterModes modes() const { return mModes; }

  // setters:
  void setAntialiasing(bool enabled);
  void setMode(PainterMode mode, bool enabled=true);
  void setModes(PainterModes modes);

  // methods hiding non-virtual base class functions (QPainter bug workarounds):
  bool begin(QPaintDevice *device);
  void setPen(const QPen &pen);
  void setPen(const QColor &color);
  void setPen(Qt::PenStyle penStyle);
  void drawLine(const QLineF &line);
  void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));}
  void save();
  void restore();

  // non-virtual methods:
  void makeNonCosmetic();

protected:
  // property members:
  PainterModes mModes;
  bool mIsAntialiasing;

  // non-property members:
  QStack<bool> mAntialiasingStack;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(HPainter::PainterModes)


class H_LIB_DECL HLayer : public QObject
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(HPlot* parentPlot READ parentPlot)
  Q_PROPERTY(QString name READ name)
  Q_PROPERTY(int index READ index)
  Q_PROPERTY(QList<HLayerable*> children READ children)
  Q_PROPERTY(bool visible READ visible WRITE setVisible)
  /// \endcond
public:
  HLayer(HPlot* parentPlot, const QString &layerName);
  ~HLayer();

  // getters:
  HPlot *parentPlot() const { return mParentPlot; }
  QString name() const { return mName; }
  int index() const { return mIndex; }
  QList<HLayerable*> children() const { return mChildren; }
  bool visible() const { return mVisible; }

  // setters:
  void setVisible(bool visible);

protected:
  // property members:
  HPlot *mParentPlot;
  QString mName;
  int mIndex;
  QList<HLayerable*> mChildren;
  bool mVisible;

  // non-virtual methods:
  void addChild(HLayerable *layerable, bool prepend);
  void removeChild(HLayerable *layerable);

private:
  Q_DISABLE_COPY(HLayer)

  friend class HPlot;
  friend class HLayerable;
};

class H_LIB_DECL HLayerable : public QObject
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(bool visible READ visible WRITE setVisible)
  Q_PROPERTY(HPlot* parentPlot READ parentPlot)
  Q_PROPERTY(HLayerable* parentLayerable READ parentLayerable)
  Q_PROPERTY(HLayer* layer READ layer WRITE setLayer NOTIFY layerChanged)
  Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
  /// \endcond
public:
  HLayerable(HPlot *plot, QString targetLayer="", HLayerable *parentLayerable=0);
  ~HLayerable();

  // getters:
  bool visible() const { return mVisible; }
  HPlot *parentPlot() const { return mParentPlot; }
  HLayerable *parentLayerable() const { return mParentLayerable.data(); }
  HLayer *layer() const { return mLayer; }
  bool antialiased() const { return mAntialiased; }

  // setters:
  void setVisible(bool on);
  Q_SLOT bool setLayer(HLayer *layer);
  bool setLayer(const QString &layerName);
  void setAntialiased(bool enabled);

  // introduced virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  // non-property methods:
  bool realVisibility() const;

signals:
  void layerChanged(HLayer *newLayer);

protected:
  // property members:
  bool mVisible;
  HPlot *mParentPlot;
  QPointer<HLayerable> mParentLayerable;
  HLayer *mLayer;
  bool mAntialiased;

  // introduced virtual methods:
  virtual void parentPlotInitialized(HPlot *parentPlot);
  virtual HMP::Interaction selectionCategory() const;
  virtual QRect clipRect() const;
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const = 0;
  virtual void draw(HPainter *painter) = 0;
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

  // non-property methods:
  void initializeParentPlot(HPlot *parentPlot);
  void setParentLayerable(HLayerable* parentLayerable);
  bool moveToLayer(HLayer *layer, bool prepend);
  void applyAntialiasingHint(HPainter *painter, bool localAntialiased, HMP::AntialiasedElement overrideElement) const;

private:
  Q_DISABLE_COPY(HLayerable)

  friend class HPlot;
  friend class HAxisRect;
};


class H_LIB_DECL HRange
{
public:
  double lower, upper;

  HRange();
  HRange(double lower, double upper);

  bool operator==(const HRange& other) { return lower == other.lower && upper == other.upper; }
  bool operator!=(const HRange& other) { return !(*this == other); }

  HRange &operator+=(const double& value) { lower+=value; upper+=value; return *this; }
  HRange &operator-=(const double& value) { lower-=value; upper-=value; return *this; }
  HRange &operator*=(const double& value) { lower*=value; upper*=value; return *this; }
  HRange &operator/=(const double& value) { lower/=value; upper/=value; return *this; }
  friend inline const HRange operator+(const HRange&, double);
  friend inline const HRange operator+(double, const HRange&);
  friend inline const HRange operator-(const HRange& range, double value);
  friend inline const HRange operator*(const HRange& range, double value);
  friend inline const HRange operator*(double value, const HRange& range);
  friend inline const HRange operator/(const HRange& range, double value);

  double size() const;
  double center() const;
  void normalize();
  void expand(const HRange &otherRange);
  HRange expanded(const HRange &otherRange) const;
  HRange sanitizedForLogScale() const;
  HRange sanitizedForLinScale() const;
  bool contains(double value) const;

  static bool validRange(double lower, double upper);
  static bool validRange(const HRange &range);
  static const double minRange; //1e-280;
  static const double maxRange; //1e280;

};
Q_DECLARE_TYPEINFO(HRange, Q_MOVABLE_TYPE);

inline const HRange operator+(const HRange& range, double value)
{
  HRange result(range);
  result += value;
  return result;
}

inline const HRange operator+(double value, const HRange& range)
{
  HRange result(range);
  result += value;
  return result;
}

inline const HRange operator-(const HRange& range, double value)
{
  HRange result(range);
  result -= value;
  return result;
}

inline const HRange operator*(const HRange& range, double value)
{
  HRange result(range);
  result *= value;
  return result;
}

inline const HRange operator*(double value, const HRange& range)
{
  HRange result(range);
  result *= value;
  return result;
}

inline const HRange operator/(const HRange& range, double value)
{
  HRange result(range);
  result /= value;
  return result;
}


class H_LIB_DECL HMarginGroup : public QObject
{
  Q_OBJECT
public:
  HMarginGroup(HPlot *parentPlot);
  ~HMarginGroup();

  // non-virtual methods:
  QList<HLayoutElement*> elements(HMP::MarginSide side) const { return mChildren.value(side); }
  bool isEmpty() const;
  void clear();

protected:
  // non-property members:
  HPlot *mParentPlot;
  QHash<HMP::MarginSide, QList<HLayoutElement*> > mChildren;

  // non-virtual methods:
  int commonMargin(HMP::MarginSide side) const;
  void addChild(HMP::MarginSide side, HLayoutElement *element);
  void removeChild(HMP::MarginSide side, HLayoutElement *element);

private:
  Q_DISABLE_COPY(HMarginGroup)

  friend class HLayoutElement;
};


class H_LIB_DECL HLayoutElement : public HLayerable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(HLayout* layout READ layout)
  Q_PROPERTY(QRect rect READ rect)
  Q_PROPERTY(QRect outerRect READ outerRect WRITE setOuterRect)
  Q_PROPERTY(QMargins margins READ margins WRITE setMargins)
  Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins)
  Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize)
  Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
  /// \endcond
public:
  enum UpdatePhase { upPreparation ///< Phase used for any type of preparation that needs to be done before margin calculation and layout
                     ,upMargins    ///< Phase in which the margins are calculated and set
                     ,upLayout     ///< Final phase in which the layout system places the rects of the elements
                   };
  Q_ENUMS(UpdatePhase)

  explicit HLayoutElement(HPlot *parentPlot=0);
  virtual ~HLayoutElement();

  // getters:
  HLayout *layout() const { return mParentLayout; }
  QRect rect() const { return mRect; }
  QRect outerRect() const { return mOuterRect; }
  QMargins margins() const { return mMargins; }
  QMargins minimumMargins() const { return mMinimumMargins; }
  HMP::MarginSides autoMargins() const { return mAutoMargins; }
  QSize minimumSize() const { return mMinimumSize; }
  QSize maximumSize() const { return mMaximumSize; }
  HMarginGroup *marginGroup(HMP::MarginSide side) const { return mMarginGroups.value(side, (HMarginGroup*)0); }
  QHash<HMP::MarginSide, HMarginGroup*> marginGroups() const { return mMarginGroups; }

  // setters:
  void setOuterRect(const QRect &rect);
  void setMargins(const QMargins &margins);
  void setMinimumMargins(const QMargins &margins);
  void setAutoMargins(HMP::MarginSides sides);
  void setMinimumSize(const QSize &size);
  void setMinimumSize(int width, int height);
  void setMaximumSize(const QSize &size);
  void setMaximumSize(int width, int height);
  void setMarginGroup(HMP::MarginSides sides, HMarginGroup *group);

  // introduced virtual methods:
  virtual void update(UpdatePhase phase);
  virtual QSize minimumSizeHint() const;
  virtual QSize maximumSizeHint() const;
  virtual QList<HLayoutElement*> elements(bool recursive) const;

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

protected:
  // property members:
  HLayout *mParentLayout;
  QSize mMinimumSize, mMaximumSize;
  QRect mRect, mOuterRect;
  QMargins mMargins, mMinimumMargins;
  HMP::MarginSides mAutoMargins;
  QHash<HMP::MarginSide, HMarginGroup*> mMarginGroups;

  // introduced virtual methods:
  virtual int calculateAutoMargin(HMP::MarginSide side);
  // events:
  virtual void mousePressEvent(QMouseEvent *event) {Q_UNUSED(event)}
  virtual void mouseMoveEvent(QMouseEvent *event) {Q_UNUSED(event)}
  virtual void mouseReleaseEvent(QMouseEvent *event) {Q_UNUSED(event)}
  virtual void mouseDoubleClickEvent(QMouseEvent *event) {Q_UNUSED(event)}
  virtual void wheelEvent(QWheelEvent *event) {Q_UNUSED(event)}

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const { Q_UNUSED(painter) }
  virtual void draw(HPainter *painter) { Q_UNUSED(painter) }
  virtual void parentPlotInitialized(HPlot *parentPlot);

private:
  Q_DISABLE_COPY(HLayoutElement)

  friend class HPlot;
  friend class HLayout;
  friend class HMarginGroup;
};


class H_LIB_DECL HLayout : public HLayoutElement
{
  Q_OBJECT
public:
  explicit HLayout();

  // reimplemented virtual methods:
  virtual void update(UpdatePhase phase);
  virtual QList<HLayoutElement*> elements(bool recursive) const;

  // introduced virtual methods:
  virtual int elementCount() const = 0;
  virtual HLayoutElement* elementAt(int index) const = 0;
  virtual HLayoutElement* takeAt(int index) = 0;
  virtual bool take(HLayoutElement* element) = 0;
  virtual void simplify();

  // non-virtual methods:
  bool removeAt(int index);
  bool remove(HLayoutElement* element);
  void clear();

protected:
  // introduced virtual methods:
  virtual void updateLayout();

  // non-virtual methods:
  void sizeConstraintsChanged() const;
  void adoptElement(HLayoutElement *el);
  void releaseElement(HLayoutElement *el);
  QVector<int> getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const;

private:
  Q_DISABLE_COPY(HLayout)
  friend class HLayoutElement;
};


class H_LIB_DECL HLayoutGrid : public HLayout
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(int rowCount READ rowCount)
  Q_PROPERTY(int columnCount READ columnCount)
  Q_PROPERTY(QList<double> columnStretchFactors READ columnStretchFactors WRITE setColumnStretchFactors)
  Q_PROPERTY(QList<double> rowStretchFactors READ rowStretchFactors WRITE setRowStretchFactors)
  Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing)
  Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing)
  /// \endcond
public:
  explicit HLayoutGrid();
  virtual ~HLayoutGrid();

  // getters:
  int rowCount() const;
  int columnCount() const;
  QList<double> columnStretchFactors() const { return mColumnStretchFactors; }
  QList<double> rowStretchFactors() const { return mRowStretchFactors; }
  int columnSpacing() const { return mColumnSpacing; }
  int rowSpacing() const { return mRowSpacing; }

  // setters:
  void setColumnStretchFactor(int column, double factor);
  void setColumnStretchFactors(const QList<double> &factors);
  void setRowStretchFactor(int row, double factor);
  void setRowStretchFactors(const QList<double> &factors);
  void setColumnSpacing(int pixels);
  void setRowSpacing(int pixels);

  // reimplemented virtual methods:
  virtual void updateLayout();
  virtual int elementCount() const;
  virtual HLayoutElement* elementAt(int index) const;
  virtual HLayoutElement* takeAt(int index);
  virtual bool take(HLayoutElement* element);
  virtual QList<HLayoutElement*> elements(bool recursive) const;
  virtual void simplify();
  virtual QSize minimumSizeHint() const;
  virtual QSize maximumSizeHint() const;

  // non-virtual methods:
  HLayoutElement *element(int row, int column) const;
  bool addElement(int row, int column, HLayoutElement *element);
  bool hasElement(int row, int column);
  void expandTo(int newRowCount, int newColumnCount);
  void insertRow(int newIndex);
  void insertColumn(int newIndex);

protected:
  // property members:
  QList<QList<HLayoutElement*> > mElements;
  QList<double> mColumnStretchFactors;
  QList<double> mRowStretchFactors;
  int mColumnSpacing, mRowSpacing;

  // non-virtual methods:
  void getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const;
  void getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const;

private:
  Q_DISABLE_COPY(HLayoutGrid)
};


class H_LIB_DECL HLayoutInset : public HLayout
{
  Q_OBJECT
public:
  enum InsetPlacement { ipFree            ///< The element may be positioned/sized arbitrarily, see \ref setInsetRect
                        ,ipBorderAligned  ///< The element is aligned to one of the layout sides, see \ref setInsetAlignment
                      };

  explicit HLayoutInset();
  virtual ~HLayoutInset();

  // getters:
  InsetPlacement insetPlacement(int index) const;
  Qt::Alignment insetAlignment(int index) const;
  QRectF insetRect(int index) const;

  // setters:
  void setInsetPlacement(int index, InsetPlacement placement);
  void setInsetAlignment(int index, Qt::Alignment alignment);
  void setInsetRect(int index, const QRectF &rect);

  // reimplemented virtual methods:
  virtual void updateLayout();
  virtual int elementCount() const;
  virtual HLayoutElement* elementAt(int index) const;
  virtual HLayoutElement* takeAt(int index);
  virtual bool take(HLayoutElement* element);
  virtual void simplify() {}
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  // non-virtual methods:
  void addElement(HLayoutElement *element, Qt::Alignment alignment);
  void addElement(HLayoutElement *element, const QRectF &rect);

protected:
  // property members:
  QList<HLayoutElement*> mElements;
  QList<InsetPlacement> mInsetPlacement;
  QList<Qt::Alignment> mInsetAlignment;
  QList<QRectF> mInsetRect;

private:
  Q_DISABLE_COPY(HLayoutInset)
};


class H_LIB_DECL HLineEnding
{
  Q_GADGET
public:

  Q_ENUMS(EndingStyle)
  enum EndingStyle { esNone          ///< No ending decoration
                     ,esFlatArrow    ///< A filled arrow head with a straight/flat back (a triangle)
                     ,esSpikeArrow   ///< A filled arrow head with an indented back
                     ,esLineArrow    ///< A non-filled arrow head with open back
                     ,esDisc         ///< A filled circle
                     ,esSquare       ///< A filled square
                     ,esDiamond      ///< A filled diamond (45° rotated square)
                     ,esBar          ///< A bar perpendicular to the line
                     ,esHalfBar      ///< A bar perpendicular to the line, pointing out to only one side (to which side can be changed with \ref setInverted)
                     ,esSkewedBar    ///< A bar that is skewed (skew controllable via \ref setLength)
                   };

  HLineEnding();
  HLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false);

  // getters:
  EndingStyle style() const { return mStyle; }
  double width() const { return mWidth; }
  double length() const { return mLength; }
  bool inverted() const { return mInverted; }

  // setters:
  void setStyle(EndingStyle style);
  void setWidth(double width);
  void setLength(double length);
  void setInverted(bool inverted);

  // non-property methods:
  double boundingDistance() const;
  double realLength() const;
  void draw(HPainter *painter, const QVector2D &pos, const QVector2D &dir) const;
  void draw(HPainter *painter, const QVector2D &pos, double angle) const;

protected:
  // property members:
  EndingStyle mStyle;
  double mWidth, mLength;
  bool mInverted;
};
Q_DECLARE_TYPEINFO(HLineEnding, Q_MOVABLE_TYPE);


class H_LIB_DECL HGrid :public HLayerable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(bool subGridVisible READ subGridVisible WRITE setSubGridVisible)
  Q_PROPERTY(bool antialiasedSubGrid READ antialiasedSubGrid WRITE setAntialiasedSubGrid)
  Q_PROPERTY(bool antialiasedZeroLine READ antialiasedZeroLine WRITE setAntialiasedZeroLine)
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen)
  Q_PROPERTY(QPen zeroLinePen READ zeroLinePen WRITE setZeroLinePen)
  /// \endcond
public:
  HGrid(HAxis *parentAxis);

  // getters:
  bool subGridVisible() const { return mSubGridVisible; }
  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }
  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }
  QPen pen() const { return mPen; }
  QPen subGridPen() const { return mSubGridPen; }
  QPen zeroLinePen() const { return mZeroLinePen; }

  // setters:
  void setSubGridVisible(bool visible);
  void setAntialiasedSubGrid(bool enabled);
  void setAntialiasedZeroLine(bool enabled);
  void setPen(const QPen &pen);
  void setSubGridPen(const QPen &pen);
  void setZeroLinePen(const QPen &pen);

protected:
  // property members:
  bool mSubGridVisible;
  bool mAntialiasedSubGrid, mAntialiasedZeroLine;
  QPen mPen, mSubGridPen, mZeroLinePen;
  // non-property members:
  HAxis *mParentAxis;

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual void draw(HPainter *painter);

  // non-virtual methods:
  void drawGridLines(HPainter *painter) const;
  void drawSubGridLines(HPainter *painter) const;

  friend class HAxis;
};


class H_LIB_DECL HAxis : public HLayerable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(AxisType axisType READ axisType)
  Q_PROPERTY(HAxisRect* axisRect READ axisRect)
  Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY scaleTypeChanged)
  Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase)
  Q_PROPERTY(HRange range READ range WRITE setRange NOTIFY rangeChanged)
  Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)
  Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
  Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
  Q_PROPERTY(bool autoTickLabels READ autoTickLabels WRITE setAutoTickLabels)
  Q_PROPERTY(bool autoTickStep READ autoTickStep WRITE setAutoTickStep)
  Q_PROPERTY(bool autoSubTicks READ autoSubTicks WRITE setAutoSubTicks)
  Q_PROPERTY(bool ticks READ ticks WRITE setTicks)
  Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels)
  Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding)
  Q_PROPERTY(LabelType tickLabelType READ tickLabelType WRITE setTickLabelType)
  Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont)
  Q_PROPERTY(QColor tickLabelColor READ tickLabelColor WRITE setTickLabelColor)
  Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation)
  Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat)
  Q_PROPERTY(Qt::TimeSpec dateTimeSpec READ dateTimeSpec WRITE setDateTimeSpec)
  Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat)
  Q_PROPERTY(int numberPrecision READ numberPrecision WRITE setNumberPrecision)
  Q_PROPERTY(double tickStep READ tickStep WRITE setTickStep)
  Q_PROPERTY(QVector<double> tickVector READ tickVector WRITE setTickVector)
  Q_PROPERTY(QVector<QString> tickVectorLabels READ tickVectorLabels WRITE setTickVectorLabels)
  Q_PROPERTY(int tickLengthIn READ tickLengthIn WRITE setTickLengthIn)
  Q_PROPERTY(int tickLengthOut READ tickLengthOut WRITE setTickLengthOut)
  Q_PROPERTY(int subTickCount READ subTickCount WRITE setSubTickCount)
  Q_PROPERTY(int subTickLengthIn READ subTickLengthIn WRITE setSubTickLengthIn)
  Q_PROPERTY(int subTickLengthOut READ subTickLengthOut WRITE setSubTickLengthOut)
  Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen)
  Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen)
  Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen)
  Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont)
  Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor)
  Q_PROPERTY(QString label READ label WRITE setLabel)
  Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
  Q_PROPERTY(int padding READ padding WRITE setPadding)
  Q_PROPERTY(int offset READ offset WRITE setOffset)
  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged)
  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged)
  Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont)
  Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)
  Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor)
  Q_PROPERTY(QColor selectedLabelColor READ selectedLabelColor WRITE setSelectedLabelColor)
  Q_PROPERTY(QPen selectedBasePen READ selectedBasePen WRITE setSelectedBasePen)
  Q_PROPERTY(QPen selectedTickPen READ selectedTickPen WRITE setSelectedTickPen)
  Q_PROPERTY(QPen selectedSubTickPen READ selectedSubTickPen WRITE setSelectedSubTickPen)
  Q_PROPERTY(HLineEnding lowerEnding READ lowerEnding WRITE setLowerEnding)
  Q_PROPERTY(HLineEnding upperEnding READ upperEnding WRITE setUpperEnding)
  Q_PROPERTY(HGrid* grid READ grid)
  /// \endcond
public:

  enum AxisType { atLeft    = 0x01  ///< <tt>0x01</tt> Axis is vertical and on the left side of the axis rect
                  ,atRight  = 0x02  ///< <tt>0x02</tt> Axis is vertical and on the right side of the axis rect
                  ,atTop    = 0x04  ///< <tt>0x04</tt> Axis is horizontal and on the top side of the axis rect
                  ,atBottom = 0x08  ///< <tt>0x08</tt> Axis is horizontal and on the bottom side of the axis rect
                };
  Q_FLAGS(AxisType AxisTypes)
  Q_DECLARE_FLAGS(AxisTypes, AxisType)

  enum LabelType { ltNumber    ///< Tick coordinate is regarded as normal number and will be displayed as such. (see \ref setNumberFormat)
                   ,ltDateTime ///< Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displayed and formatted as such. (for details, see \ref setDateTimeFormat)
                 };
  Q_ENUMS(LabelType)

  enum ScaleType { stLinear       ///< Linear scaling
                   ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power (see \ref setScaleLogBase).
                 };
  Q_ENUMS(ScaleType)

  enum SelectablePart { spNone        = 0      ///< None of the selectable parts
                        ,spAxis       = 0x001  ///< The axis backbone and tick marks
                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)
                        ,spAxisLabel  = 0x004  ///< The axis label
                      };
  Q_FLAGS(SelectablePart SelectableParts)
  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)

  explicit HAxis(HAxisRect *parent, AxisType type);
  virtual ~HAxis();

  // getters:
  AxisType axisType() const { return mAxisType; }
  HAxisRect *axisRect() const { return mAxisRect; }
  ScaleType scaleType() const { return mScaleType; }
  double scaleLogBase() const { return mScaleLogBase; }
  const HRange range() const { return mRange; }
  bool rangeReversed() const { return mRangeReversed; }
  bool autoTicks() const { return mAutoTicks; }
  int autoTickCount() const { return mAutoTickCount; }
  bool autoTickLabels() const { return mAutoTickLabels; }
  bool autoTickStep() const { return mAutoTickStep; }
  bool autoSubTicks() const { return mAutoSubTicks; }
  bool ticks() const { return mTicks; }
  bool tickLabels() const { return mTickLabels; }
  int tickLabelPadding() const;
  LabelType tickLabelType() const { return mTickLabelType; }
  QFont tickLabelFont() const { return mTickLabelFont; }
  QColor tickLabelColor() const { return mTickLabelColor; }
  double tickLabelRotation() const;
  QString dateTimeFormat() const { return mDateTimeFormat; }
  Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
  QString numberFormat() const;
  int numberPrecision() const { return mNumberPrecision; }
  double tickStep() const { return mTickStep; }
  QVector<double> tickVector() const { return mTickVector; }
  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
  int tickLengthIn() const;
  int tickLengthOut() const;
  int subTickCount() const { return mSubTickCount; }
  int subTickLengthIn() const;
  int subTickLengthOut() const;
  QPen basePen() const { return mBasePen; }
  QPen tickPen() const { return mTickPen; }
  QPen subTickPen() const { return mSubTickPen; }
  QFont labelFont() const { return mLabelFont; }
  QColor labelColor() const { return mLabelColor; }
  QString label() const { return mLabel; }
  int labelPadding() const;
  int padding() const { return mPadding; }
  int offset() const;
  SelectableParts selectedParts() const { return mSelectedParts; }
  SelectableParts selectableParts() const { return mSelectableParts; }
  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
  QFont selectedLabelFont() const { return mSelectedLabelFont; }
  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
  QColor selectedLabelColor() const { return mSelectedLabelColor; }
  QPen selectedBasePen() const { return mSelectedBasePen; }
  QPen selectedTickPen() const { return mSelectedTickPen; }
  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
  HLineEnding lowerEnding() const;
  HLineEnding upperEnding() const;
  HGrid *grid() const { return mGrid; }

  // setters:
  Q_SLOT void setScaleType(HAxis::ScaleType type);
  void setScaleLogBase(double base);
  Q_SLOT void setRange(const HRange &range);
  void setRange(double lower, double upper);
  void setRange(double position, double size, Qt::AlignmentFlag alignment);
  void setRangeLower(double lower);
  void setRangeUpper(double upper);
  void setRangeReversed(bool reversed);
  void setAutoTicks(bool on);
  void setAutoTickCount(int approximateCount);
  void setAutoTickLabels(bool on);
  void setAutoTickStep(bool on);
  void setAutoSubTicks(bool on);
  void setTicks(bool show);
  void setTickLabels(bool show);
  void setTickLabelPadding(int padding);
  void setTickLabelType(LabelType type);
  void setTickLabelFont(const QFont &font);
  void setTickLabelColor(const QColor &color);
  void setTickLabelRotation(double degrees);
  void setDateTimeFormat(const QString &format);
  void setDateTimeSpec(const Qt::TimeSpec &timeSpec);
  void setNumberFormat(const QString &formatCode);
  void setNumberPrecision(int precision);
  void setTickStep(double step);
  void setTickVector(const QVector<double> &vec);
  void setTickVectorLabels(const QVector<QString> &vec);
  void setTickLength(int inside, int outside=0);
  void setTickLengthIn(int inside);
  void setTickLengthOut(int outside);
  void setSubTickCount(int count);
  void setSubTickLength(int inside, int outside=0);
  void setSubTickLengthIn(int inside);
  void setSubTickLengthOut(int outside);
  void setBasePen(const QPen &pen);
  void setTickPen(const QPen &pen);
  void setSubTickPen(const QPen &pen);
  void setLabelFont(const QFont &font);
  void setLabelColor(const QColor &color);
  void setLabel(const QString &str);
  void setLabelPadding(int padding);
  void setPadding(int padding);
  void setOffset(int offset);
  void setSelectedTickLabelFont(const QFont &font);
  void setSelectedLabelFont(const QFont &font);
  void setSelectedTickLabelColor(const QColor &color);
  void setSelectedLabelColor(const QColor &color);
  void setSelectedBasePen(const QPen &pen);
  void setSelectedTickPen(const QPen &pen);
  void setSelectedSubTickPen(const QPen &pen);
  Q_SLOT void setSelectableParts(const HAxis::SelectableParts &selectableParts);
  Q_SLOT void setSelectedParts(const HAxis::SelectableParts &selectedParts);
  void setLowerEnding(const HLineEnding &ending);
  void setUpperEnding(const HLineEnding &ending);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  // non-property methods:
  Qt::Orientation orientation() const { return mOrientation; }
  void moveRange(double diff);
  void scaleRange(double factor, double center);
  void setScaleRatio(const HAxis *otherAxis, double ratio=1.0);
  void rescale(bool onlyVisiblePlottables=false);
  double pixelToCoord(double value) const;
  double coordToPixel(double value) const;
  SelectablePart getPartAt(const QPointF &pos) const;
  QList<HAbstractPlottable*> plottables() const;
  QList<HGraph*> graphs() const;
  QList<HAbstractItem*> items() const;

  static AxisType marginSideToAxisType(HMP::MarginSide side);
  static Qt::Orientation orientation(AxisType type) { return type==atBottom||type==atTop ? Qt::Horizontal : Qt::Vertical; }
  static AxisType opposite(AxisType type);

signals:
  void ticksRequest();
  void rangeChanged(const HRange &newRange);
  void rangeChanged(const HRange &newRange, const HRange &oldRange);
  void scaleTypeChanged(HAxis::ScaleType scaleType);
  void selectionChanged(const HAxis::SelectableParts &parts);
  void selectableChanged(const HAxis::SelectableParts &parts);

protected:
  // property members:
  // axis base:
  AxisType mAxisType;
  HAxisRect *mAxisRect;
  //int mOffset; // in HAxisPainter
  int mPadding;
  Qt::Orientation mOrientation;
  SelectableParts mSelectableParts, mSelectedParts;
  QPen mBasePen, mSelectedBasePen;
  //HLineEnding mLowerEnding, mUpperEnding; // in HAxisPainter
  // axis label:
  //int mLabelPadding; // in HAxisPainter
  QString mLabel;
  QFont mLabelFont, mSelectedLabelFont;
  QColor mLabelColor, mSelectedLabelColor;
  // tick labels:
  //int mTickLabelPadding; // in HAxisPainter
  bool mTickLabels, mAutoTickLabels;
  //double mTickLabelRotation; // in HAxisPainter
  LabelType mTickLabelType;
  QFont mTickLabelFont, mSelectedTickLabelFont;
  QColor mTickLabelColor, mSelectedTickLabelColor;
  QString mDateTimeFormat;
  Qt::TimeSpec mDateTimeSpec;
  int mNumberPrecision;
  char mNumberFormatChar;
  bool mNumberBeautifulPowers;
  //bool mNumberMultiplyCross; // HAxisPainter
  // ticks and subticks:
  bool mTicks;
  double mTickStep;
  int mSubTickCount, mAutoTickCount;
  bool mAutoTicks, mAutoTickStep, mAutoSubTicks;
  //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // HAxisPainter
  QPen mTickPen, mSelectedTickPen;
  QPen mSubTickPen, mSelectedSubTickPen;
  // scale and range:
  HRange mRange;
  bool mRangeReversed;
  ScaleType mScaleType;
  double mScaleLogBase, mScaleLogBaseLogInv;

  // non-property members:
  HGrid *mGrid;
  HAxisPainterPrivate *mAxisPainter;
  int mLowestVisibleTick, mHighestVisibleTick;
  QVector<double> mTickVector;
  QVector<QString> mTickVectorLabels;
  QVector<double> mSubTickVector;
  bool mCachedMarginValid;
  int mCachedMargin;

  // introduced virtual methods:
  virtual void setupTickVectors();
  virtual void generateAutoTicks();
  virtual int calculateAutoSubTickCount(double tickStep) const;
  virtual int calculateMargin();

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual void draw(HPainter *painter);
  virtual HMP::Interaction selectionCategory() const;
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

  // non-virtual methods:
  void visibleTickBounds(int &lowIndex, int &highIndex) const;
  double baseLog(double value) const;
  double basePow(double value) const;
  QPen getBasePen() const;
  QPen getTickPen() const;
  QPen getSubTickPen() const;
  QFont getTickLabelFont() const;
  QFont getLabelFont() const;
  QColor getTickLabelColor() const;
  QColor getLabelColor() const;

private:
  Q_DISABLE_COPY(HAxis)

  friend class HPlot;
  friend class HGrid;
  friend class HAxisRect;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(HAxis::SelectableParts)
Q_DECLARE_OPERATORS_FOR_FLAGS(HAxis::AxisTypes)
Q_DECLARE_METATYPE(HAxis::SelectablePart)


class HAxisPainterPrivate
{
public:
  explicit HAxisPainterPrivate(HPlot *parentPlot);
  virtual ~HAxisPainterPrivate();

  virtual void draw(HPainter *painter);
  virtual int size() const;
  void clearCache();

  QRect axisSelectionBox() const { return mAxisSelectionBox; }
  QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; }
  QRect labelSelectionBox() const { return mLabelSelectionBox; }

  // public property members:
  HAxis::AxisType type;
  QPen basePen;
  HLineEnding lowerEnding, upperEnding; // directly accessed by HAxis setters/getters
  int labelPadding; // directly accessed by HAxis setters/getters
  QFont labelFont;
  QColor labelColor;
  QString label;
  int tickLabelPadding; // directly accessed by HAxis setters/getters
  double tickLabelRotation; // directly accessed by HAxis setters/getters
  bool substituteExponent;
  bool numberMultiplyCross; // directly accessed by HAxis setters/getters
  int tickLengthIn, tickLengthOut, subTickLengthIn, subTickLengthOut; // directly accessed by HAxis setters/getters
  QPen tickPen, subTickPen;
  QFont tickLabelFont;
  QColor tickLabelColor;
  QRect alignmentRect, viewportRect;
  double offset; // directly accessed by HAxis setters/getters
  bool abbreviateDecimalPowers;
  bool reversedEndings;

  QVector<double> subTickPositions;
  QVector<double> tickPositions;
  QVector<QString> tickLabels;

protected:
  struct CachedLabel
  {
    QPointF offset;
    QPixmap pixmap;
  };
  struct TickLabelData
  {
    QString basePart, expPart;
    QRect baseBounds, expBounds, totalBounds, rotatedTotalBounds;
    QFont baseFont, expFont;
  };
  HPlot *mParentPlot;
  QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters
  QCache<QString, CachedLabel> mLabelCache;
  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;

  virtual QByteArray generateLabelParameterHash() const;

  virtual void placeTickLabel(HPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);
  virtual void drawTickLabel(HPainter *painter, double x, double y, const TickLabelData &labelData) const;
  virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const;
  virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const;
  virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
};


class H_LIB_DECL HAbstractPlottable : public HLayerable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QString name READ name WRITE setName)
  Q_PROPERTY(bool antialiasedFill READ antialiasedFill WRITE setAntialiasedFill)
  Q_PROPERTY(bool antialiasedScatters READ antialiasedScatters WRITE setAntialiasedScatters)
  Q_PROPERTY(bool antialiasedErrorBars READ antialiasedErrorBars WRITE setAntialiasedErrorBars)
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  Q_PROPERTY(HAxis* keyAxis READ keyAxis WRITE setKeyAxis)
  Q_PROPERTY(HAxis* valueAxis READ valueAxis WRITE setValueAxis)
  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
  /// \endcond
public:
  HAbstractPlottable(HAxis *keyAxis, HAxis *valueAxis);

  // getters:
  QString name() const { return mName; }
  bool antialiasedFill() const { return mAntialiasedFill; }
  bool antialiasedScatters() const { return mAntialiasedScatters; }
  bool antialiasedErrorBars() const { return mAntialiasedErrorBars; }
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  QBrush brush() const { return mBrush; }
  QBrush selectedBrush() const { return mSelectedBrush; }
  HAxis *keyAxis() const { return mKeyAxis.data(); }
  HAxis *valueAxis() const { return mValueAxis.data(); }
  bool selectable() const { return mSelectable; }
  bool selected() const { return mSelected; }

  // setters:
  void setName(const QString &name);
  void setAntialiasedFill(bool enabled);
  void setAntialiasedScatters(bool enabled);
  void setAntialiasedErrorBars(bool enabled);
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setSelectedBrush(const QBrush &brush);
  void setKeyAxis(HAxis *axis);
  void setValueAxis(HAxis *axis);
  Q_SLOT void setSelectable(bool selectable);
  Q_SLOT void setSelected(bool selected);

  // introduced virtual methods:
  virtual void clearData() = 0;
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
  virtual bool addToLegend();
  virtual bool removeFromLegend() const;

  // non-property methods:
  void rescaleAxes(bool onlyEnlarge=false) const;
  void rescaleKeyAxis(bool onlyEnlarge=false) const;
  void rescaleValueAxis(bool onlyEnlarge=false) const;

signals:
  void selectionChanged(bool selected);
  void selectableChanged(bool selectable);

protected:

  enum SignDomain { sdNegative  ///< The negative sign domain, i.e. numbers smaller than zero
                    ,sdBoth     ///< Both sign domains, including zero, i.e. all (rational) numbers
                    ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero
                  };

  // property members:
  QString mName;
  bool mAntialiasedFill, mAntialiasedScatters, mAntialiasedErrorBars;
  QPen mPen, mSelectedPen;
  QBrush mBrush, mSelectedBrush;
  QPointer<HAxis> mKeyAxis, mValueAxis;
  bool mSelectable, mSelected;

  // reimplemented virtual methods:
  virtual QRect clipRect() const;
  virtual void draw(HPainter *painter) = 0;
  virtual HMP::Interaction selectionCategory() const;
  void applyDefaultAntialiasingHint(HPainter *painter) const;
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

  // introduced virtual methods:
  virtual void drawLegendIcon(HPainter *painter, const QRectF &rect) const = 0;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;

  // non-virtual methods:
  void coordsToPixels(double key, double value, double &x, double &y) const;
  const QPointF coordsToPixels(double key, double value) const;
  void pixelsToCoords(double x, double y, double &key, double &value) const;
  void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;
  QPen mainPen() const;
  QBrush mainBrush() const;
  void applyFillAntialiasingHint(HPainter *painter) const;
  void applyScattersAntialiasingHint(HPainter *painter) const;
  void applyErrorBarsAntialiasingHint(HPainter *painter) const;
  double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;

private:
  Q_DISABLE_COPY(HAbstractPlottable)

  friend class HPlot;
  friend class HAxis;
  friend class HPlottableLegendItem;
};


class H_LIB_DECL HItemAnchor
{
public:
  HItemAnchor(HPlot *parentPlot, HAbstractItem *parentItem, const QString name, int anchorId=-1);
  virtual ~HItemAnchor();

  // getters:
  QString name() const { return mName; }
  virtual QPointF pixelPoint() const;

protected:
  // property members:
  QString mName;

  // non-property members:
  HPlot *mParentPlot;
  HAbstractItem *mParentItem;
  int mAnchorId;
  QSet<HItemPosition*> mChildren;

  // introduced virtual methods:
  virtual HItemPosition *toHItemPosition() { return 0; }

  // non-virtual methods:
  void addChild(HItemPosition* pos); // called from pos when this anchor is set as parent
  void removeChild(HItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted

private:
  Q_DISABLE_COPY(HItemAnchor)

  friend class HItemPosition;
};



class H_LIB_DECL HItemPosition : public HItemAnchor
{
public:

  enum PositionType { ptAbsolute        ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.
                      ,ptViewportRatio  ///< Static positioning given by a fraction of the viewport size. For example, if you call setCoords(0, 0), the position will be at the top
                                        ///< left corner of the viewport/widget. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and
                                        ///< vertically at the top of the viewport/widget, etc.
                      ,ptAxisRectRatio  ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect). For example, if you call setCoords(0, 0), the position will be at the top
                                        ///< left corner of the axis rect. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and
                                        ///< vertically at the top of the axis rect, etc. You can also go beyond the axis rect by providing negative coordinates or coordinates larger than 1.
                      ,ptPlotCoords     ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes).
                    };

  HItemPosition(HPlot *parentPlot, HAbstractItem *parentItem, const QString name);
  virtual ~HItemPosition();

  // getters:
  PositionType type() const { return mPositionType; }
  HItemAnchor *parentAnchor() const { return mParentAnchor; }
  double key() const { return mKey; }
  double value() const { return mValue; }
  QPointF coords() const { return QPointF(mKey, mValue); }
  HAxis *keyAxis() const { return mKeyAxis.data(); }
  HAxis *valueAxis() const { return mValueAxis.data(); }
  HAxisRect *axisRect() const;
  virtual QPointF pixelPoint() const;

  // setters:
  void setType(PositionType type);
  bool setParentAnchor(HItemAnchor *parentAnchor, bool keepPixelPosition=false);
  void setCoords(double key, double value);
  void setCoords(const QPointF &coords);
  void setAxes(HAxis* keyAxis, HAxis* valueAxis);
  void setAxisRect(HAxisRect *axisRect);
  void setPixelPoint(const QPointF &pixelPoint);

protected:
  // property members:
  PositionType mPositionType;
  QPointer<HAxis> mKeyAxis, mValueAxis;
  QPointer<HAxisRect> mAxisRect;
  double mKey, mValue;
  HItemAnchor *mParentAnchor;

  // reimplemented virtual methods:
  virtual HItemPosition *toHItemPosition() { return this; }

private:
  Q_DISABLE_COPY(HItemPosition)

};


class H_LIB_DECL HAbstractItem : public HLayerable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)
  Q_PROPERTY(HAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)
  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
  /// \endcond
public:
  HAbstractItem(HPlot *parentPlot);
  virtual ~HAbstractItem();

  // getters:
  bool clipToAxisRect() const { return mClipToAxisRect; }
  HAxisRect *clipAxisRect() const;
  bool selectable() const { return mSelectable; }
  bool selected() const { return mSelected; }

  // setters:
  void setClipToAxisRect(bool clip);
  void setClipAxisRect(HAxisRect *rect);
  Q_SLOT void setSelectable(bool selectable);
  Q_SLOT void setSelected(bool selected);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;

  // non-virtual methods:
  QList<HItemPosition*> positions() const { return mPositions; }
  QList<HItemAnchor*> anchors() const { return mAnchors; }
  HItemPosition *position(const QString &name) const;
  HItemAnchor *anchor(const QString &name) const;
  bool hasAnchor(const QString &name) const;

signals:
  void selectionChanged(bool selected);
  void selectableChanged(bool selectable);

protected:
  // property members:
  bool mClipToAxisRect;
  QPointer<HAxisRect> mClipAxisRect;
  QList<HItemPosition*> mPositions;
  QList<HItemAnchor*> mAnchors;
  bool mSelectable, mSelected;

  // reimplemented virtual methods:
  virtual HMP::Interaction selectionCategory() const;
  virtual QRect clipRect() const;
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual void draw(HPainter *painter) = 0;
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

  // introduced virtual methods:
  virtual QPointF anchorPixelPoint(int anchorId) const;

  // non-virtual methods:
  double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
  double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const;
  HItemPosition *createPosition(const QString &name);
  HItemAnchor *createAnchor(const QString &name, int anchorId);

private:
  Q_DISABLE_COPY(HAbstractItem)

  friend class HPlot;
  friend class HItemAnchor;
};


class H_LIB_DECL HPlot : public QWidget
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QRect viewport READ viewport WRITE setViewport)
  Q_PROPERTY(QPixmap background READ background WRITE setBackground)
  Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)
  Q_PROPERTY(HLayoutGrid* plotLayout READ plotLayout)
  Q_PROPERTY(bool autoAddPlottableToLegend READ autoAddPlottableToLegend WRITE setAutoAddPlottableToLegend)
  Q_PROPERTY(int selectionTolerance READ selectionTolerance WRITE setSelectionTolerance)
  Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag)
  Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier)
  /// \endcond
public:

  enum LayerInsertMode { limBelow  ///< Layer is inserted below other layer
                         ,limAbove ///< Layer is inserted above other layer
                       };
  Q_ENUMS(LayerInsertMode)

  enum RefreshPriority { rpImmediate ///< The HPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot
                         ,rpQueued   ///< Queues the refresh such that it is performed at a slightly delayed point in time after the replot, by calling QWidget::update() after the replot
                         ,rpHint     ///< Whether to use immediate repaint or queued update depends on whether the plotting hint \ref HMP::phForceRepaint is set, see \ref setPlottingHints.
                       };

  explicit HPlot(QWidget *parent = 0);
  virtual ~HPlot();

  // getters:
  QRect viewport() const { return mViewport; }
  QPixmap background() const { return mBackgroundPixmap; }
  bool backgroundScaled() const { return mBackgroundScaled; }
  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
  HLayoutGrid *plotLayout() const { return mPlotLayout; }
  HMP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; }
  HMP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; }
  bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; }
  const HMP::Interactions interactions() const { return mInteractions; }
  int selectionTolerance() const { return mSelectionTolerance; }
  bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; }
  HMP::PlottingHints plottingHints() const { return mPlottingHints; }
  Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; }

  // setters:
  void setViewport(const QRect &rect);
  void setBackground(const QPixmap &pm);
  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
  void setBackground(const QBrush &brush);
  void setBackgroundScaled(bool scaled);
  void setBackgroundScaledMode(Qt::AspectRatioMode mode);
  void setAntialiasedElements(const HMP::AntialiasedElements &antialiasedElements);
  void setAntialiasedElement(HMP::AntialiasedElement antialiasedElement, bool enabled=true);
  void setNotAntialiasedElements(const HMP::AntialiasedElements &notAntialiasedElements);
  void setNotAntialiasedElement(HMP::AntialiasedElement notAntialiasedElement, bool enabled=true);
  void setAutoAddPlottableToLegend(bool on);
  void setInteractions(const HMP::Interactions &interactions);
  void setInteraction(const HMP::Interaction &interaction, bool enabled=true);
  void setSelectionTolerance(int pixels);
  void setNoAntialiasingOnDrag(bool enabled);
  void setPlottingHints(const HMP::PlottingHints &hints);
  void setPlottingHint(HMP::PlottingHint hint, bool enabled=true);
  void setMultiSelectModifier(Qt::KeyboardModifier modifier);

  // non-property methods:
  // plottable interface:
  HAbstractPlottable *plottable(int index);
  HAbstractPlottable *plottable();
  bool addPlottable(HAbstractPlottable *plottable);
  bool removePlottable(HAbstractPlottable *plottable);
  bool removePlottable(int index);
  int clearPlottables();
  int plottableCount() const;
  QList<HAbstractPlottable*> selectedPlottables() const;
  HAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const;
  bool hasPlottable(HAbstractPlottable *plottable) const;

  // specialized interface for HGraph:
  HGraph *graph(int index) const;
  HGraph *graph() const;
  HGraph *addGraph(HAxis *keyAxis=0, HAxis *valueAxis=0);
  bool removeGraph(HGraph *graph);
  bool removeGraph(int index);
  int clearGraphs();
  int graphCount() const;
  QList<HGraph*> selectedGraphs() const;

  // item interface:
  HAbstractItem *item(int index) const;
  HAbstractItem *item() const;
  bool addItem(HAbstractItem* item);
  bool removeItem(HAbstractItem *item);
  bool removeItem(int index);
  int clearItems();
  int itemCount() const;
  QList<HAbstractItem*> selectedItems() const;
  HAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const;
  bool hasItem(HAbstractItem *item) const;

  // layer interface:
  HLayer *layer(const QString &name) const;
  HLayer *layer(int index) const;
  HLayer *currentLayer() const;
  bool setCurrentLayer(const QString &name);
  bool setCurrentLayer(HLayer *layer);
  int layerCount() const;
  bool addLayer(const QString &name, HLayer *otherLayer=0, LayerInsertMode insertMode=limAbove);
  bool removeLayer(HLayer *layer);
  bool moveLayer(HLayer *layer, HLayer *otherLayer, LayerInsertMode insertMode=limAbove);

  // axis rect/layout interface:
  int axisRectCount() const;
  HAxisRect* axisRect(int index=0) const;
  QList<HAxisRect*> axisRects() const;
  HLayoutElement* layoutElementAt(const QPointF &pos) const;
  Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false);

  QList<HAxis*> selectedAxes() const;
  QList<HLegend*> selectedLegends() const;
  Q_SLOT void deselectAll();

  bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator="", const QString &pdfTitle="");
  bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
  bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
  bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
  bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
  QPixmap toPixmap(int width=0, int height=0, double scale=1.0);
  void toPainter(HPainter *painter, int width=0, int height=0);
  Q_SLOT void replot(HPlot::RefreshPriority refreshPriority=HPlot::rpHint);

  HAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
  HLegend *legend;

signals:
  void mouseDoubleClick(QMouseEvent *event);
  void mousePress(QMouseEvent *event);
  void mouseMove(QMouseEvent *event);
  void mouseRelease(QMouseEvent *event);
  void mouseWheel(QWheelEvent *event);

  void plottableClick(HAbstractPlottable *plottable, QMouseEvent *event);
  void plottableDoubleClick(HAbstractPlottable *plottable, QMouseEvent *event);
  void itemClick(HAbstractItem *item, QMouseEvent *event);
  void itemDoubleClick(HAbstractItem *item, QMouseEvent *event);
  void axisClick(HAxis *axis, HAxis::SelectablePart part, QMouseEvent *event);
  void axisDoubleClick(HAxis *axis, HAxis::SelectablePart part, QMouseEvent *event);
  void legendClick(HLegend *legend, HAbstractLegendItem *item, QMouseEvent *event);
  void legendDoubleClick(HLegend *legend,  HAbstractLegendItem *item, QMouseEvent *event);
  void titleClick(QMouseEvent *event, HPlotTitle *title);
  void titleDoubleClick(QMouseEvent *event, HPlotTitle *title);

  void selectionChangedByUser();
  void beforeReplot();
  void afterReplot();

protected:
  // property members:
  QRect mViewport;
  HLayoutGrid *mPlotLayout;
  bool mAutoAddPlottableToLegend;
  QList<HAbstractPlottable*> mPlottables;
  QList<HGraph*> mGraphs; // extra list of plottables also in mPlottables that are of type HGraph
  QList<HAbstractItem*> mItems;
  QList<HLayer*> mLayers;
  HMP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements;
  HMP::Interactions mInteractions;
  int mSelectionTolerance;
  bool mNoAntialiasingOnDrag;
  QBrush mBackgroundBrush;
  QPixmap mBackgroundPixmap;
  QPixmap mScaledBackgroundPixmap;
  bool mBackgroundScaled;
  Qt::AspectRatioMode mBackgroundScaledMode;
  HLayer *mCurrentLayer;
  HMP::PlottingHints mPlottingHints;
  Qt::KeyboardModifier mMultiSelectModifier;

  // non-property members:
  QPixmap mPaintBuffer;
  QPoint mMousePressPos;
  QPointer<HLayoutElement> mMouseEventElement;
  bool mReplotting;

  // reimplemented virtual methods:
  virtual QSize minimumSizeHint() const;
  virtual QSize sizeHint() const;
  virtual void paintEvent(QPaintEvent *event);
  virtual void resizeEvent(QResizeEvent *event);
  virtual void mouseDoubleClickEvent(QMouseEvent *event);
  virtual void mousePressEvent(QMouseEvent *event);
  virtual void mouseMoveEvent(QMouseEvent *event);
  virtual void mouseReleaseEvent(QMouseEvent *event);
  virtual void wheelEvent(QWheelEvent *event);

  // introduced virtual methods:
  virtual void draw(HPainter *painter);
  virtual void axisRemoved(HAxis *axis);
  virtual void legendRemoved(HLegend *legend);

  // non-virtual methods:
  void updateLayerIndices() const;
  HLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const;
  void drawBackground(HPainter *painter);

  friend class HLegend;
  friend class HAxis;
  friend class HLayer;
  friend class HAxisRect;
};


class H_LIB_DECL HColorGradient
{
  Q_GADGET
public:

  enum ColorInterpolation { ciRGB  ///< Color channels red, green and blue are linearly interpolated
                            ,ciHSV ///< Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the shortest angle distance)
                          };
  Q_ENUMS(ColorInterpolation)

  enum GradientPreset { gpGrayscale  ///< Continuous lightness from black to white (suited for non-biased data representation)
                        ,gpHot       ///< Continuous lightness from black over firey colors to white (suited for non-biased data representation)
                        ,gpCold      ///< Continuous lightness from black over icey colors to white (suited for non-biased data representation)
                        ,gpNight     ///< Continuous lightness from black over weak blueish colors to white (suited for non-biased data representation)
                        ,gpCandy     ///< Blue over pink to white
                        ,gpGeography ///< Colors suitable to represent different elevations on geographical maps
                        ,gpIon       ///< Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allows more precise magnitude estimates)
                        ,gpThermal   ///< Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white
                        ,gpPolar     ///< Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle and red for positive values
                        ,gpSpectrum  ///< An approximation of the visible light spectrum (creates banding illusion but allows more precise magnitude estimates)
                        ,gpJet       ///< Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion but allows more precise magnitude estimates)
                        ,gpHues      ///< Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and phases, see \ref setPeriodic)
                      };
  Q_ENUMS(GradientPreset)

  HColorGradient(GradientPreset preset=gpCold);
  bool operator==(const HColorGradient &other) const;
  bool operator!=(const HColorGradient &other) const { return !(*this == other); }

  // getters:
  int levelCount() const { return mLevelCount; }
  QMap<double, QColor> colorStops() const { return mColorStops; }
  ColorInterpolation colorInterpolation() const { return mColorInterpolation; }
  bool periodic() const { return mPeriodic; }

  // setters:
  void setLevelCount(int n);
  void setColorStops(const QMap<double, QColor> &colorStops);
  void setColorStopAt(double position, const QColor &color);
  void setColorInterpolation(ColorInterpolation interpolation);
  void setPeriodic(bool enabled);

  // non-property methods:
  void colorize(const double *data, const HRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false);
  QRgb color(double position, const HRange &range, bool logarithmic=false);
  void loadPreset(GradientPreset preset);
  void clearColorStops();
  HColorGradient inverted() const;

protected:
  void updateColorBuffer();

  // property members:
  int mLevelCount;
  QMap<double, QColor> mColorStops;
  ColorInterpolation mColorInterpolation;
  bool mPeriodic;

  // non-property members:
  QVector<QRgb> mColorBuffer;
  bool mColorBufferInvalidated;
};


class H_LIB_DECL HAxisRect : public HLayoutElement
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPixmap background READ background WRITE setBackground)
  Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)
  Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
  Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
  /// \endcond
public:
  explicit HAxisRect(HPlot *parentPlot, bool setupDefaultAxes=true);
  virtual ~HAxisRect();

  // getters:
  QPixmap background() const { return mBackgroundPixmap; }
  bool backgroundScaled() const { return mBackgroundScaled; }
  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
  Qt::Orientations rangeDrag() const { return mRangeDrag; }
  Qt::Orientations rangeZoom() const { return mRangeZoom; }
  HAxis *rangeDragAxis(Qt::Orientation orientation);
  HAxis *rangeZoomAxis(Qt::Orientation orientation);
  double rangeZoomFactor(Qt::Orientation orientation);

  // setters:
  void setBackground(const QPixmap &pm);
  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
  void setBackground(const QBrush &brush);
  void setBackgroundScaled(bool scaled);
  void setBackgroundScaledMode(Qt::AspectRatioMode mode);
  void setRangeDrag(Qt::Orientations orientations);
  void setRangeZoom(Qt::Orientations orientations);
  void setRangeDragAxes(HAxis *horizontal, HAxis *vertical);
  void setRangeZoomAxes(HAxis *horizontal, HAxis *vertical);
  void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
  void setRangeZoomFactor(double factor);

  // non-property methods:
  int axisCount(HAxis::AxisType type) const;
  HAxis *axis(HAxis::AxisType type, int index=0) const;
  QList<HAxis*> axes(HAxis::AxisTypes types) const;
  QList<HAxis*> axes() const;
  HAxis *addAxis(HAxis::AxisType type);
  QList<HAxis*> addAxes(HAxis::AxisTypes types);
  bool removeAxis(HAxis *axis);
  HLayoutInset *insetLayout() const { return mInsetLayout; }

  void setupFullAxesBox(bool connectRanges=false);
  QList<HAbstractPlottable*> plottables() const;
  QList<HGraph*> graphs() const;
  QList<HAbstractItem*> items() const;

  // read-only interface imitating a QRect:
  int left() const { return mRect.left(); }
  int right() const { return mRect.right(); }
  int top() const { return mRect.top(); }
  int bottom() const { return mRect.bottom(); }
  int width() const { return mRect.width(); }
  int height() const { return mRect.height(); }
  QSize size() const { return mRect.size(); }
  QPoint topLeft() const { return mRect.topLeft(); }
  QPoint topRight() const { return mRect.topRight(); }
  QPoint bottomLeft() const { return mRect.bottomLeft(); }
  QPoint bottomRight() const { return mRect.bottomRight(); }
  QPoint center() const { return mRect.center(); }

  // reimplemented virtual methods:
  virtual void update(UpdatePhase phase);
  virtual QList<HLayoutElement*> elements(bool recursive) const;

protected:
  // property members:
  QBrush mBackgroundBrush;
  QPixmap mBackgroundPixmap;
  QPixmap mScaledBackgroundPixmap;
  bool mBackgroundScaled;
  Qt::AspectRatioMode mBackgroundScaledMode;
  HLayoutInset *mInsetLayout;
  Qt::Orientations mRangeDrag, mRangeZoom;
  QPointer<HAxis> mRangeDragHorzAxis, mRangeDragVertAxis, mRangeZoomHorzAxis, mRangeZoomVertAxis;
  double mRangeZoomFactorHorz, mRangeZoomFactorVert;
  // non-property members:
  HRange mDragStartHorzRange, mDragStartVertRange;
  HMP::AntialiasedElements mAADragBackup, mNotAADragBackup;
  QPoint mDragStart;
  bool mDragging;
  QHash<HAxis::AxisType, QList<HAxis*> > mAxes;

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual void draw(HPainter *painter);
  virtual int calculateAutoMargin(HMP::MarginSide side);
  // events:
  virtual void mousePressEvent(QMouseEvent *event);
  virtual void mouseMoveEvent(QMouseEvent *event);
  virtual void mouseReleaseEvent(QMouseEvent *event);
  virtual void wheelEvent(QWheelEvent *event);

  // non-property methods:
  void drawBackground(HPainter *painter);
  void updateAxesOffset(HAxis::AxisType type);

private:
  Q_DISABLE_COPY(HAxisRect)

  friend class HPlot;
};


class H_LIB_DECL HAbstractLegendItem : public HLayoutElement
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(HLegend* parentLegend READ parentLegend)
  Q_PROPERTY(QFont font READ font WRITE setFont)
  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectionChanged)
  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectableChanged)
  /// \endcond
public:
  explicit HAbstractLegendItem(HLegend *parent);

  // getters:
  HLegend *parentLegend() const { return mParentLegend; }
  QFont font() const { return mFont; }
  QColor textColor() const { return mTextColor; }
  QFont selectedFont() const { return mSelectedFont; }
  QColor selectedTextColor() const { return mSelectedTextColor; }
  bool selectable() const { return mSelectable; }
  bool selected() const { return mSelected; }

  // setters:
  void setFont(const QFont &font);
  void setTextColor(const QColor &color);
  void setSelectedFont(const QFont &font);
  void setSelectedTextColor(const QColor &color);
  Q_SLOT void setSelectable(bool selectable);
  Q_SLOT void setSelected(bool selected);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

signals:
  void selectionChanged(bool selected);
  void selectableChanged(bool selectable);

protected:
  // property members:
  HLegend *mParentLegend;
  QFont mFont;
  QColor mTextColor;
  QFont mSelectedFont;
  QColor mSelectedTextColor;
  bool mSelectable, mSelected;

  // reimplemented virtual methods:
  virtual HMP::Interaction selectionCategory() const;
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual QRect clipRect() const;
  virtual void draw(HPainter *painter) = 0;
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

private:
  Q_DISABLE_COPY(HAbstractLegendItem)

  friend class HLegend;
};


class H_LIB_DECL HPlottableLegendItem : public HAbstractLegendItem
{
  Q_OBJECT
public:
  HPlottableLegendItem(HLegend *parent, HAbstractPlottable *plottable);

  // getters:
  HAbstractPlottable *plottable() { return mPlottable; }

protected:
  // property members:
  HAbstractPlottable *mPlottable;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual QSize minimumSizeHint() const;

  // non-virtual methods:
  QPen getIconBorderPen() const;
  QColor getTextColor() const;
  QFont getFont() const;
};


class H_LIB_DECL HLegend : public HLayoutGrid
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QFont font READ font WRITE setFont)
  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
  Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
  Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding)
  Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen)
  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectionChanged)
  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectableChanged)
  Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen)
  Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
  /// \endcond
public:

  enum SelectablePart { spNone       = 0x000  ///< <tt>0x000</tt> None
                        ,spLegendBox  = 0x001 ///< <tt>0x001</tt> The legend box (frame)
                        ,spItems      = 0x002 ///< <tt>0x002</tt> Legend items individually (see \ref selectedItems)
                      };
  Q_FLAGS(SelectablePart SelectableParts)
  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)

  explicit HLegend();
  virtual ~HLegend();

  // getters:
  QPen borderPen() const { return mBorderPen; }
  QBrush brush() const { return mBrush; }
  QFont font() const { return mFont; }
  QColor textColor() const { return mTextColor; }
  QSize iconSize() const { return mIconSize; }
  int iconTextPadding() const { return mIconTextPadding; }
  QPen iconBorderPen() const { return mIconBorderPen; }
  SelectableParts selectableParts() const { return mSelectableParts; }
  SelectableParts selectedParts() const;
  QPen selectedBorderPen() const { return mSelectedBorderPen; }
  QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }
  QBrush selectedBrush() const { return mSelectedBrush; }
  QFont selectedFont() const { return mSelectedFont; }
  QColor selectedTextColor() const { return mSelectedTextColor; }

  // setters:
  void setBorderPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setFont(const QFont &font);
  void setTextColor(const QColor &color);
  void setIconSize(const QSize &size);
  void setIconSize(int width, int height);
  void setIconTextPadding(int padding);
  void setIconBorderPen(const QPen &pen);
  Q_SLOT void setSelectableParts(const SelectableParts &selectableParts);
  Q_SLOT void setSelectedParts(const SelectableParts &selectedParts);
  void setSelectedBorderPen(const QPen &pen);
  void setSelectedIconBorderPen(const QPen &pen);
  void setSelectedBrush(const QBrush &brush);
  void setSelectedFont(const QFont &font);
  void setSelectedTextColor(const QColor &color);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  // non-virtual methods:
  HAbstractLegendItem *item(int index) const;
  HPlottableLegendItem *itemWithPlottable(const HAbstractPlottable *plottable) const;
  int itemCount() const;
  bool hasItem(HAbstractLegendItem *item) const;
  bool hasItemWithPlottable(const HAbstractPlottable *plottable) const;
  bool addItem(HAbstractLegendItem *item);
  bool removeItem(int index);
  bool removeItem(HAbstractLegendItem *item);
  void clearItems();
  QList<HAbstractLegendItem*> selectedItems() const;

signals:
  void selectionChanged(HLegend::SelectableParts parts);
  void selectableChanged(HLegend::SelectableParts parts);

protected:
  // property members:
  QPen mBorderPen, mIconBorderPen;
  QBrush mBrush;
  QFont mFont;
  QColor mTextColor;
  QSize mIconSize;
  int mIconTextPadding;
  SelectableParts mSelectedParts, mSelectableParts;
  QPen mSelectedBorderPen, mSelectedIconBorderPen;
  QBrush mSelectedBrush;
  QFont mSelectedFont;
  QColor mSelectedTextColor;

  // reimplemented virtual methods:
  virtual void parentPlotInitialized(HPlot *parentPlot);
  virtual HMP::Interaction selectionCategory() const;
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual void draw(HPainter *painter);
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

  // non-virtual methods:
  QPen getBorderPen() const;
  QBrush getBrush() const;

private:
  Q_DISABLE_COPY(HLegend)

  friend class HPlot;
  friend class HAbstractLegendItem;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(HLegend::SelectableParts)
Q_DECLARE_METATYPE(HLegend::SelectablePart)


class H_LIB_DECL HPlotTitle : public HLayoutElement
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QString text READ text WRITE setText)
  Q_PROPERTY(QFont font READ font WRITE setFont)
  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
  /// \endcond
public:
  explicit HPlotTitle(HPlot *parentPlot);
  explicit HPlotTitle(HPlot *parentPlot, const QString &text);

  // getters:
  QString text() const { return mText; }
  QFont font() const { return mFont; }
  QColor textColor() const { return mTextColor; }
  QFont selectedFont() const { return mSelectedFont; }
  QColor selectedTextColor() const { return mSelectedTextColor; }
  bool selectable() const { return mSelectable; }
  bool selected() const { return mSelected; }

  // setters:
  void setText(const QString &text);
  void setFont(const QFont &font);
  void setTextColor(const QColor &color);
  void setSelectedFont(const QFont &font);
  void setSelectedTextColor(const QColor &color);
  Q_SLOT void setSelectable(bool selectable);
  Q_SLOT void setSelected(bool selected);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

signals:
  void selectionChanged(bool selected);
  void selectableChanged(bool selectable);

protected:
  // property members:
  QString mText;
  QFont mFont;
  QColor mTextColor;
  QFont mSelectedFont;
  QColor mSelectedTextColor;
  QRect mTextBoundingRect;
  bool mSelectable, mSelected;

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  virtual void draw(HPainter *painter);
  virtual QSize minimumSizeHint() const;
  virtual QSize maximumSizeHint() const;
  // events:
  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
  virtual void deselectEvent(bool *selectionStateChanged);

  // non-virtual methods:
  QFont mainFont() const;
  QColor mainTextColor() const;

private:
  Q_DISABLE_COPY(HPlotTitle)
};


class HColorScaleAxisRectPrivate : public HAxisRect
{
  Q_OBJECT
public:
  explicit HColorScaleAxisRectPrivate(HColorScale *parentColorScale);
protected:
  HColorScale *mParentColorScale;
  QImage mGradientImage;
  bool mGradientImageInvalidated;
  // re-using some methods of HAxisRect to make them available to friend class HColorScale
  using HAxisRect::calculateAutoMargin;
  using HAxisRect::mousePressEvent;
  using HAxisRect::mouseMoveEvent;
  using HAxisRect::mouseReleaseEvent;
  using HAxisRect::wheelEvent;
  using HAxisRect::update;
  virtual void draw(HPainter *painter);
  void updateGradientImage();
  Q_SLOT void axisSelectionChanged(HAxis::SelectableParts selectedParts);
  Q_SLOT void axisSelectableChanged(HAxis::SelectableParts selectableParts);
  friend class HColorScale;
};


class H_LIB_DECL HColorScale : public HLayoutElement
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(HAxis::AxisType type READ type WRITE setType)
  Q_PROPERTY(HRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)
  Q_PROPERTY(HAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)
  Q_PROPERTY(HColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)
  Q_PROPERTY(QString label READ label WRITE setLabel)
  Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth)
  Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag)
  Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom)
  /// \endcond
public:
  explicit HColorScale(HPlot *parentPlot);
  virtual ~HColorScale();

  // getters:
  HAxis *axis() const { return mColorAxis.data(); }
  HAxis::AxisType type() const { return mType; }
  HRange dataRange() const { return mDataRange; }
  HAxis::ScaleType dataScaleType() const { return mDataScaleType; }
  HColorGradient gradient() const { return mGradient; }
  QString label() const;
  int barWidth () const { return mBarWidth; }
  bool rangeDrag() const;
  bool rangeZoom() const;

  // setters:
  void setType(HAxis::AxisType type);
  Q_SLOT void setDataRange(const HRange &dataRange);
  Q_SLOT void setDataScaleType(HAxis::ScaleType scaleType);
  Q_SLOT void setGradient(const HColorGradient &gradient);
  void setLabel(const QString &str);
  void setBarWidth(int width);
  void setRangeDrag(bool enabled);
  void setRangeZoom(bool enabled);

  // non-property methods:
  QList<HColorMap*> colorMaps() const;
  void rescaleDataRange(bool onlyVisibleMaps);

  // reimplemented virtual methods:
  virtual void update(UpdatePhase phase);

signals:
  void dataRangeChanged(HRange newRange);
  void dataScaleTypeChanged(HAxis::ScaleType scaleType);
  void gradientChanged(HColorGradient newGradient);

protected:
  // property members:
  HAxis::AxisType mType;
  HRange mDataRange;
  HAxis::ScaleType mDataScaleType;
  HColorGradient mGradient;
  int mBarWidth;

  // non-property members:
  QPointer<HColorScaleAxisRectPrivate> mAxisRect;
  QPointer<HAxis> mColorAxis;

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(HPainter *painter) const;
  // events:
  virtual void mousePressEvent(QMouseEvent *event);
  virtual void mouseMoveEvent(QMouseEvent *event);
  virtual void mouseReleaseEvent(QMouseEvent *event);
  virtual void wheelEvent(QWheelEvent *event);

private:
  Q_DISABLE_COPY(HColorScale)

  friend class HColorScaleAxisRectPrivate;
};

class H_LIB_DECL HData
{
public:
  HData();
  HData(double key, double value);
  double key, value;
  double keyErrorPlus, keyErrorMinus;
  double valueErrorPlus, valueErrorMinus;
};
Q_DECLARE_TYPEINFO(HData, Q_MOVABLE_TYPE);

typedef QMap<double, HData> HDataMap;
typedef QMapIterator<double, HData> HDataMapIterator;
typedef QMutableMapIterator<double, HData> HDataMutableMapIterator;


class H_LIB_DECL HGraph : public HAbstractPlottable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle)
  Q_PROPERTY(HScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle)
  Q_PROPERTY(ErrorType errorType READ errorType WRITE setErrorType)
  Q_PROPERTY(QPen errorPen READ errorPen WRITE setErrorPen)
  Q_PROPERTY(double errorBarSize READ errorBarSize WRITE setErrorBarSize)
  Q_PROPERTY(bool errorBarSkipSymbol READ errorBarSkipSymbol WRITE setErrorBarSkipSymbol)
  Q_PROPERTY(HGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph)
  Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling)
  /// \endcond
public:

  enum LineStyle { lsNone        ///< data points are not connected with any lines (e.g. data only represented
                                 ///< with symbols according to the scatter style, see \ref setScatterStyle)
                   ,lsLine       ///< data points are connected by a straight line
                   ,lsStepLeft   ///< line is drawn as steps where the step height is the value of the left data point
                   ,lsStepRight  ///< line is drawn as steps where the step height is the value of the right data point
                   ,lsStepCenter ///< line is drawn as steps where the step is in between two data points
                   ,lsImpulse    ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line
                 };
  Q_ENUMS(LineStyle)

  enum ErrorType { etNone   ///< No error bars are shown
                   ,etKey   ///< Error bars for the key dimension of the data point are shown
                   ,etValue ///< Error bars for the value dimension of the data point are shown
                   ,etBoth  ///< Error bars for both key and value dimensions of the data point are shown
                 };
  Q_ENUMS(ErrorType)

  explicit HGraph(HAxis *keyAxis, HAxis *valueAxis);
  virtual ~HGraph();

  // getters:
  HDataMap *data() const { return mData; }
  LineStyle lineStyle() const { return mLineStyle; }
  HScatterStyle scatterStyle() const { return mScatterStyle; }
  ErrorType errorType() const { return mErrorType; }
  QPen errorPen() const { return mErrorPen; }
  double errorBarSize() const { return mErrorBarSize; }
  bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
  HGraph *channelFillGraph() const { return mChannelFillGraph.data(); }
  bool adaptiveSampling() const { return mAdaptiveSampling; }

  // setters:
  void setData(HDataMap *data, bool copy=false);
  void setData(const QVector<double> &key, const QVector<double> &value);
  void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError);
  void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus);
  void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError);
  void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
  void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError);
  void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
  void setLineStyle(LineStyle ls);
  void setScatterStyle(const HScatterStyle &style);
  void setErrorType(ErrorType errorType);
  void setErrorPen(const QPen &pen);
  void setErrorBarSize(double size);
  void setErrorBarSkipSymbol(bool enabled);
  void setChannelFillGraph(HGraph *targetGraph);
  void setAdaptiveSampling(bool enabled);

  // non-property methods:
  void addData(const HDataMap &dataMap);
  void addData(const HData &data);
  void addData(double key, double value);
  void addData(const QVector<double> &keys, const QVector<double> &values);
  void removeDataBefore(double key);
  void removeDataAfter(double key);
  void removeData(double fromKey, double toKey);
  void removeData(double key);

  // reimplemented virtual methods:
  virtual void clearData();
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
  using HAbstractPlottable::rescaleAxes;
  using HAbstractPlottable::rescaleKeyAxis;
  using HAbstractPlottable::rescaleValueAxis;
  void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
  void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
  void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface

protected:
  // property members:
  HDataMap *mData;
  QPen mErrorPen;
  LineStyle mLineStyle;
  HScatterStyle mScatterStyle;
  ErrorType mErrorType;
  double mErrorBarSize;
  bool mErrorBarSkipSymbol;
  QPointer<HGraph> mChannelFillGraph;
  bool mAdaptiveSampling;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual void drawLegendIcon(HPainter *painter, const QRectF &rect) const;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface

  // introduced virtual methods:
  virtual void drawFill(HPainter *painter, QVector<QPointF> *lineData) const;
  virtual void drawScatterPlot(HPainter *painter, QVector<HData> *scatterData) const;
  virtual void drawLinePlot(HPainter *painter, QVector<QPointF> *lineData) const;
  virtual void drawImpulsePlot(HPainter *painter, QVector<QPointF> *lineData) const;

  // non-virtual methods:
  void getPreparedData(QVector<HData> *lineData, QVector<HData> *scatterData) const;
  void getPlotData(QVector<QPointF> *lineData, QVector<HData> *scatterData) const;
  void getScatterPlotData(QVector<HData> *scatterData) const;
  void getLinePlotData(QVector<QPointF> *linePixelData, QVector<HData> *scatterData) const;
  void getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<HData> *scatterData) const;
  void getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<HData> *scatterData) const;
  void getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<HData> *scatterData) const;
  void getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<HData> *scatterData) const;
  void drawError(HPainter *painter, double x, double y, const HData &data) const;
  void getVisibleDataBounds(HDataMap::const_iterator &lower, HDataMap::const_iterator &upper) const;
  int countDataInBounds(const HDataMap::const_iterator &lower, const HDataMap::const_iterator &upper, int maxCount) const;
  void addFillBasePoints(QVector<QPointF> *lineData) const;
  void removeFillBasePoints(QVector<QPointF> *lineData) const;
  QPointF lowerFillBasePoint(double lowerKey) const;
  QPointF upperFillBasePoint(double upperKey) const;
  const QPolygonF getChannelFillPolygon(const QVector<QPointF> *lineData) const;
  int findIndexBelowX(const QVector<QPointF> *data, double x) const;
  int findIndexAboveX(const QVector<QPointF> *data, double x) const;
  int findIndexBelowY(const QVector<QPointF> *data, double y) const;
  int findIndexAboveY(const QVector<QPointF> *data, double y) const;
  double pointDistance(const QPointF &pixelPoint) const;

  friend class HPlot;
  friend class HLegend;
};


class H_LIB_DECL HCurveData
{
public:
  HCurveData();
  HCurveData(double t, double key, double value);
  double t, key, value;
};
Q_DECLARE_TYPEINFO(HCurveData, Q_MOVABLE_TYPE);

typedef QMap<double, HCurveData> HCurveDataMap;
typedef QMapIterator<double, HCurveData> HCurveDataMapIterator;
typedef QMutableMapIterator<double, HCurveData> HCurveDataMutableMapIterator;


class H_LIB_DECL HCurve : public HAbstractPlottable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(HScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle)
  Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle)
  /// \endcond
public:
  enum LineStyle { lsNone  ///< No line is drawn between data points (e.g. only scatters)
                   ,lsLine ///< Data points are connected with a straight line
                 };
  explicit HCurve(HAxis *keyAxis, HAxis *valueAxis);
  virtual ~HCurve();

  // getters:
  HCurveDataMap *data() const { return mData; }
  HScatterStyle scatterStyle() const { return mScatterStyle; }
  LineStyle lineStyle() const { return mLineStyle; }

  // setters:
  void setData(HCurveDataMap *data, bool copy=false);
  void setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value);
  void setData(const QVector<double> &key, const QVector<double> &value);
  void setScatterStyle(const HScatterStyle &style);
  void setLineStyle(LineStyle style);

  // non-property methods:
  void addData(const HCurveDataMap &dataMap);
  void addData(const HCurveData &data);
  void addData(double t, double key, double value);
  void addData(double key, double value);
  void addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values);
  void removeDataBefore(double t);
  void removeDataAfter(double t);
  void removeData(double fromt, double tot);
  void removeData(double t);

  // reimplemented virtual methods:
  virtual void clearData();
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

protected:
  // property members:
  HCurveDataMap *mData;
  HScatterStyle mScatterStyle;
  LineStyle mLineStyle;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual void drawLegendIcon(HPainter *painter, const QRectF &rect) const;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;

  // introduced virtual methods:
  virtual void drawScatterPlot(HPainter *painter, const QVector<QPointF> *pointData) const;

  // non-virtual methods:
  void getCurveData(QVector<QPointF> *lineData) const;
  double pointDistance(const QPointF &pixelPoint) const;
  QPointF outsideCoordsToPixels(double key, double value, int region, QRect axisRect) const;

  friend class HPlot;
  friend class HLegend;
};



class H_LIB_DECL HBarData
{
public:
  HBarData();
  HBarData(double key, double value);
  double key, value;
};
Q_DECLARE_TYPEINFO(HBarData, Q_MOVABLE_TYPE);

typedef QMap<double, HBarData> HBarDataMap;
typedef QMapIterator<double, HBarData> HBarDataMapIterator;
typedef QMutableMapIterator<double, HBarData> HBarDataMutableMapIterator;


class H_LIB_DECL HBars : public HAbstractPlottable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(double width READ width WRITE setWidth)
  Q_PROPERTY(HBars* barBelow READ barBelow)
  Q_PROPERTY(HBars* barAbove READ barAbove)
  /// \endcond
public:
  explicit HBars(HAxis *keyAxis, HAxis *valueAxis);
  virtual ~HBars();

  // getters:
  double width() const { return mWidth; }
  HBars *barBelow() const { return mBarBelow.data(); }
  HBars *barAbove() const { return mBarAbove.data(); }
  HBarDataMap *data() const { return mData; }

  // setters:
  void setWidth(double width);
  void setData(HBarDataMap *data, bool copy=false);
  void setData(const QVector<double> &key, const QVector<double> &value);

  // non-property methods:
  void moveBelow(HBars *bars);
  void moveAbove(HBars *bars);
  void addData(const HBarDataMap &dataMap);
  void addData(const HBarData &data);
  void addData(double key, double value);
  void addData(const QVector<double> &keys, const QVector<double> &values);
  void removeDataBefore(double key);
  void removeDataAfter(double key);
  void removeData(double fromKey, double toKey);
  void removeData(double key);

  // reimplemented virtual methods:
  virtual void clearData();
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

protected:
  // property members:
  HBarDataMap *mData;
  double mWidth;
  QPointer<HBars> mBarBelow, mBarAbove;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual void drawLegendIcon(HPainter *painter, const QRectF &rect) const;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;

  // non-virtual methods:
  QPolygonF getBarPolygon(double key, double value) const;
  double getBaseValue(double key, bool positive) const;
  static void connectBars(HBars* lower, HBars* upper);

  friend class HPlot;
  friend class HLegend;
};


class H_LIB_DECL HStatisticalBox : public HAbstractPlottable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(double key READ key WRITE setKey)
  Q_PROPERTY(double minimum READ minimum WRITE setMinimum)
  Q_PROPERTY(double lowerQuartile READ lowerQuartile WRITE setLowerQuartile)
  Q_PROPERTY(double median READ median WRITE setMedian)
  Q_PROPERTY(double upperQuartile READ upperQuartile WRITE setUpperQuartile)
  Q_PROPERTY(double maximum READ maximum WRITE setMaximum)
  Q_PROPERTY(QVector<double> outliers READ outliers WRITE setOutliers)
  Q_PROPERTY(double width READ width WRITE setWidth)
  Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth)
  Q_PROPERTY(QPen whiskerPen READ whiskerPen WRITE setWhiskerPen)
  Q_PROPERTY(QPen whiskerBarPen READ whiskerBarPen WRITE setWhiskerBarPen)
  Q_PROPERTY(QPen medianPen READ medianPen WRITE setMedianPen)
  Q_PROPERTY(HScatterStyle outlierStyle READ outlierStyle WRITE setOutlierStyle)
  /// \endcond
public:
  explicit HStatisticalBox(HAxis *keyAxis, HAxis *valueAxis);

  // getters:
  double key() const { return mKey; }
  double minimum() const { return mMinimum; }
  double lowerQuartile() const { return mLowerQuartile; }
  double median() const { return mMedian; }
  double upperQuartile() const { return mUpperQuartile; }
  double maximum() const { return mMaximum; }
  QVector<double> outliers() const { return mOutliers; }
  double width() const { return mWidth; }
  double whiskerWidth() const { return mWhiskerWidth; }
  QPen whiskerPen() const { return mWhiskerPen; }
  QPen whiskerBarPen() const { return mWhiskerBarPen; }
  QPen medianPen() const { return mMedianPen; }
  HScatterStyle outlierStyle() const { return mOutlierStyle; }

  // setters:
  void setKey(double key);
  void setMinimum(double value);
  void setLowerQuartile(double value);
  void setMedian(double value);
  void setUpperQuartile(double value);
  void setMaximum(double value);
  void setOutliers(const QVector<double> &values);
  void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum);
  void setWidth(double width);
  void setWhiskerWidth(double width);
  void setWhiskerPen(const QPen &pen);
  void setWhiskerBarPen(const QPen &pen);
  void setMedianPen(const QPen &pen);
  void setOutlierStyle(const HScatterStyle &style);

  // non-property methods:
  virtual void clearData();
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

protected:
  // property members:
  QVector<double> mOutliers;
  double mKey, mMinimum, mLowerQuartile, mMedian, mUpperQuartile, mMaximum;
  double mWidth;
  double mWhiskerWidth;
  QPen mWhiskerPen, mWhiskerBarPen, mMedianPen;
  HScatterStyle mOutlierStyle;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual void drawLegendIcon(HPainter *painter, const QRectF &rect) const;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;

  // introduced virtual methods:
  virtual void drawQuartileBox(HPainter *painter, QRectF *quartileBox=0) const;
  virtual void drawMedian(HPainter *painter) const;
  virtual void drawWhiskers(HPainter *painter) const;
  virtual void drawOutliers(HPainter *painter) const;

  friend class HPlot;
  friend class HLegend;
};


class H_LIB_DECL HColorMapData
{
public:
  HColorMapData(int keySize, int valueSize, const HRange &keyRange, const HRange &valueRange);
  ~HColorMapData();
  HColorMapData(const HColorMapData &other);
  HColorMapData &operator=(const HColorMapData &other);

  // getters:
  int keySize() const { return mKeySize; }
  int valueSize() const { return mValueSize; }
  HRange keyRange() const { return mKeyRange; }
  HRange valueRange() const { return mValueRange; }
  HRange dataBounds() const { return mDataBounds; }
  double data(double key, double value);
  double cell(int keyIndex, int valueIndex);

  // setters:
  void setSize(int keySize, int valueSize);
  void setKeySize(int keySize);
  void setValueSize(int valueSize);
  void setRange(const HRange &keyRange, const HRange &valueRange);
  void setKeyRange(const HRange &keyRange);
  void setValueRange(const HRange &valueRange);
  void setData(double key, double value, double z);
  void setCell(int keyIndex, int valueIndex, double z);

  // non-property methods:
  void recalculateDataBounds();
  void clear();
  void fill(double z);
  bool isEmpty() const { return mIsEmpty; }
  void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const;
  void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const;

protected:
  // property members:
  int mKeySize, mValueSize;
  HRange mKeyRange, mValueRange;
  bool mIsEmpty;
  // non-property members:
  double *mData;
  HRange mDataBounds;
  bool mDataModified;

  friend class HColorMap;
};


class H_LIB_DECL HColorMap : public HAbstractPlottable
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(HRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)
  Q_PROPERTY(HAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)
  Q_PROPERTY(HColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)
  Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate)
  Q_PROPERTY(bool tightBoundary READ tightBoundary WRITE setTightBoundary)
  Q_PROPERTY(HColorScale* colorScale READ colorScale WRITE setColorScale)
  /// \endcond
public:
  explicit HColorMap(HAxis *keyAxis, HAxis *valueAxis);
  virtual ~HColorMap();

  // getters:
  HColorMapData *data() const { return mMapData; }
  HRange dataRange() const { return mDataRange; }
  HAxis::ScaleType dataScaleType() const { return mDataScaleType; }
  bool interpolate() const { return mInterpolate; }
  bool tightBoundary() const { return mTightBoundary; }
  HColorGradient gradient() const { return mGradient; }
  HColorScale *colorScale() const { return mColorScale.data(); }

  // setters:
  void setData(HColorMapData *data, bool copy=false);
  Q_SLOT void setDataRange(const HRange &dataRange);
  Q_SLOT void setDataScaleType(HAxis::ScaleType scaleType);
  Q_SLOT void setGradient(const HColorGradient &gradient);
  void setInterpolate(bool enabled);
  void setTightBoundary(bool enabled);
  void setColorScale(HColorScale *colorScale);

  // non-property methods:
  void rescaleDataRange(bool recalculateDataBounds=false);
  Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18));

  // reimplemented virtual methods:
  virtual void clearData();
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

signals:
  void dataRangeChanged(HRange newRange);
  void dataScaleTypeChanged(HAxis::ScaleType scaleType);
  void gradientChanged(HColorGradient newGradient);

protected:
  // property members:
  HRange mDataRange;
  HAxis::ScaleType mDataScaleType;
  HColorMapData *mMapData;
  HColorGradient mGradient;
  bool mInterpolate;
  bool mTightBoundary;
  QPointer<HColorScale> mColorScale;
  // non-property members:
  QImage mMapImage;
  QPixmap mLegendIcon;
  bool mMapImageInvalidated;

  // introduced virtual methods:
  virtual void updateMapImage();

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual void drawLegendIcon(HPainter *painter, const QRectF &rect) const;
  virtual HRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
  virtual HRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;

  friend class HPlot;
  friend class HLegend;
};


class H_LIB_DECL HItemStraightLine : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  /// \endcond
public:
  HItemStraightLine(HPlot *parentPlot);
  virtual ~HItemStraightLine();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const point1;
  HItemPosition * const point2;

protected:
  // property members:
  QPen mPen, mSelectedPen;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);

  // non-virtual methods:
  double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const;
  QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const;
  QPen mainPen() const;
};


class H_LIB_DECL HItemLine : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(HLineEnding head READ head WRITE setHead)
  Q_PROPERTY(HLineEnding tail READ tail WRITE setTail)
  /// \endcond
public:
  HItemLine(HPlot *parentPlot);
  virtual ~HItemLine();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  HLineEnding head() const { return mHead; }
  HLineEnding tail() const { return mTail; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setHead(const HLineEnding &head);
  void setTail(const HLineEnding &tail);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const start;
  HItemPosition * const end;

protected:
  // property members:
  QPen mPen, mSelectedPen;
  HLineEnding mHead, mTail;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);

  // non-virtual methods:
  QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const;
  QPen mainPen() const;
};


class H_LIB_DECL HItemCurve : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(HLineEnding head READ head WRITE setHead)
  Q_PROPERTY(HLineEnding tail READ tail WRITE setTail)
  /// \endcond
public:
  HItemCurve(HPlot *parentPlot);
  virtual ~HItemCurve();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  HLineEnding head() const { return mHead; }
  HLineEnding tail() const { return mTail; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setHead(const HLineEnding &head);
  void setTail(const HLineEnding &tail);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const start;
  HItemPosition * const startDir;
  HItemPosition * const endDir;
  HItemPosition * const end;

protected:
  // property members:
  QPen mPen, mSelectedPen;
  HLineEnding mHead, mTail;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);

  // non-virtual methods:
  QPen mainPen() const;
};


class H_LIB_DECL HItemRect : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  /// \endcond
public:
  HItemRect(HPlot *parentPlot);
  virtual ~HItemRect();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  QBrush brush() const { return mBrush; }
  QBrush selectedBrush() const { return mSelectedBrush; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setSelectedBrush(const QBrush &brush);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const topLeft;
  HItemPosition * const bottomRight;
  HItemAnchor * const top;
  HItemAnchor * const topRight;
  HItemAnchor * const right;
  HItemAnchor * const bottom;
  HItemAnchor * const bottomLeft;
  HItemAnchor * const left;

protected:
  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};

  // property members:
  QPen mPen, mSelectedPen;
  QBrush mBrush, mSelectedBrush;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual QPointF anchorPixelPoint(int anchorId) const;

  // non-virtual methods:
  QPen mainPen() const;
  QBrush mainBrush() const;
};

class H_LIB_DECL HItemPolygon : public HAbstractItem
{
    Q_OBJECT
    /// \cond INCLUDE_QPROPERTIES
    Q_PROPERTY(QPen pen READ pen WRITE setPen)
    Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
    Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
    Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
    /// \endcond
  public:
    HItemPolygon(HPlot *parentPlot);
    virtual ~HItemPolygon();

    // getters:
    QPen pen() const { return mPen; }
    QPen selectedPen() const { return mSelectedPen; }
    QBrush brush() const { return mBrush; }
    QBrush selectedBrush() const { return mSelectedBrush; }

    // setters;
    void setPen(const QPen &pen);
    void setSelectedPen(const QPen &pen);
    void setBrush(const QBrush &brush);
    void setSelectedBrush(const QBrush &brush);

    // reimplemented virtual methods:
    virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

    QVector<HItemPosition*> mvctPos;
    QVector<HItemAnchor*> mvctAnchor;

  protected:
    enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};

    // property members:
    QPen mPen, mSelectedPen;
    QBrush mBrush, mSelectedBrush;

    // reimplemented virtual methods:
    virtual void draw(HPainter *painter);
    virtual QPointF anchorPixelPoint(int anchorId) const;

    // non-virtual methods:
    QPen mainPen() const;
    QBrush mainBrush() const;
};


class H_LIB_DECL HItemText : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QColor color READ color WRITE setColor)
  Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor)
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  Q_PROPERTY(QFont font READ font WRITE setFont)
  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
  Q_PROPERTY(QString text READ text WRITE setText)
  Q_PROPERTY(Qt::Alignment positionAlignment READ positionAlignment WRITE setPositionAlignment)
  Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment)
  Q_PROPERTY(double rotation READ rotation WRITE setRotation)
  Q_PROPERTY(QMargins padding READ padding WRITE setPadding)
  /// \endcond
public:
  HItemText(HPlot *parentPlot);
  virtual ~HItemText();

  // getters:
  QColor color() const { return mColor; }
  QColor selectedColor() const { return mSelectedColor; }
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  QBrush brush() const { return mBrush; }
  QBrush selectedBrush() const { return mSelectedBrush; }
  QFont font() const { return mFont; }
  QFont selectedFont() const { return mSelectedFont; }
  QString text() const { return mText; }
  Qt::Alignment positionAlignment() const { return mPositionAlignment; }
  Qt::Alignment textAlignment() const { return mTextAlignment; }
  double rotation() const { return mRotation; }
  QMargins padding() const { return mPadding; }

  // setters;
  void setColor(const QColor &color);
  void setSelectedColor(const QColor &color);
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setSelectedBrush(const QBrush &brush);
  void setFont(const QFont &font);
  void setSelectedFont(const QFont &font);
  void setText(const QString &text);
  void setPositionAlignment(Qt::Alignment alignment);
  void setTextAlignment(Qt::Alignment alignment);
  void setRotation(double degrees);
  void setPadding(const QMargins &padding);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const position;
  HItemAnchor * const topLeft;
  HItemAnchor * const top;
  HItemAnchor * const topRight;
  HItemAnchor * const right;
  HItemAnchor * const bottomRight;
  HItemAnchor * const bottom;
  HItemAnchor * const bottomLeft;
  HItemAnchor * const left;

protected:
  enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft};

  // property members:
  QColor mColor, mSelectedColor;
  QPen mPen, mSelectedPen;
  QBrush mBrush, mSelectedBrush;
  QFont mFont, mSelectedFont;
  QString mText;
  Qt::Alignment mPositionAlignment;
  Qt::Alignment mTextAlignment;
  double mRotation;
  QMargins mPadding;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual QPointF anchorPixelPoint(int anchorId) const;

  // non-virtual methods:
  QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const;
  QFont mainFont() const;
  QColor mainColor() const;
  QPen mainPen() const;
  QBrush mainBrush() const;
};


class H_LIB_DECL HItemEllipse : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  /// \endcond
public:
  HItemEllipse(HPlot *parentPlot);
  virtual ~HItemEllipse();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  QBrush brush() const { return mBrush; }
  QBrush selectedBrush() const { return mSelectedBrush; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setSelectedBrush(const QBrush &brush);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const topLeft;
  HItemPosition * const bottomRight;
  HItemAnchor * const topLeftRim;
  HItemAnchor * const top;
  HItemAnchor * const topRightRim;
  HItemAnchor * const right;
  HItemAnchor * const bottomRightRim;
  HItemAnchor * const bottom;
  HItemAnchor * const bottomLeftRim;
  HItemAnchor * const left;
  HItemAnchor * const center;

protected:
  enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft, aiCenter};

  // property members:
  QPen mPen, mSelectedPen;
  QBrush mBrush, mSelectedBrush;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual QPointF anchorPixelPoint(int anchorId) const;

  // non-virtual methods:
  QPen mainPen() const;
  QBrush mainBrush() const;
};


class H_LIB_DECL HItemPixmap : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
  Q_PROPERTY(bool scaled READ scaled WRITE setScaled)
  Q_PROPERTY(Qt::AspectRatioMode aspectRatioMode READ aspectRatioMode)
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  /// \endcond
public:
  HItemPixmap(HPlot *parentPlot);
  virtual ~HItemPixmap();

  // getters:
  QPixmap pixmap() const { return mPixmap; }
  bool scaled() const { return mScaled; }
  Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; }
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }

  // setters;
  void setPixmap(const QPixmap &pixmap);
  void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio);
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const topLeft;
  HItemPosition * const bottomRight;
  HItemAnchor * const top;
  HItemAnchor * const topRight;
  HItemAnchor * const right;
  HItemAnchor * const bottom;
  HItemAnchor * const bottomLeft;
  HItemAnchor * const left;

protected:
  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};

  // property members:
  QPixmap mPixmap;
  QPixmap mScaledPixmap;
  bool mScaled;
  Qt::AspectRatioMode mAspectRatioMode;
  QPen mPen, mSelectedPen;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual QPointF anchorPixelPoint(int anchorId) const;

  // non-virtual methods:
  void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false);
  QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const;
  QPen mainPen() const;
};


class H_LIB_DECL HItemTracer : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
  Q_PROPERTY(double size READ size WRITE setSize)
  Q_PROPERTY(TracerStyle style READ style WRITE setStyle)
  Q_PROPERTY(HGraph* graph READ graph WRITE setGraph)
  Q_PROPERTY(double graphKey READ graphKey WRITE setGraphKey)
  Q_PROPERTY(bool interpolating READ interpolating WRITE setInterpolating)
  /// \endcond
public:

  enum TracerStyle { tsNone        ///< The tracer is not visible
                     ,tsPlus       ///< A plus shaped crosshair with limited size
                     ,tsCrosshair  ///< A plus shaped crosshair which spans the complete axis rect
                     ,tsCircle     ///< A circle
                     ,tsSquare     ///< A square
                   };
  Q_ENUMS(TracerStyle)

  HItemTracer(HPlot *parentPlot);
  virtual ~HItemTracer();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  QBrush brush() const { return mBrush; }
  QBrush selectedBrush() const { return mSelectedBrush; }
  double size() const { return mSize; }
  TracerStyle style() const { return mStyle; }
  HGraph *graph() const { return mGraph; }
  double graphKey() const { return mGraphKey; }
  bool interpolating() const { return mInterpolating; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setBrush(const QBrush &brush);
  void setSelectedBrush(const QBrush &brush);
  void setSize(double size);
  void setStyle(TracerStyle style);
  void setGraph(HGraph *graph);
  void setGraphKey(double key);
  void setInterpolating(bool enabled);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  // non-virtual methods:
  void updatePosition();

  HItemPosition * const position;

protected:
  // property members:
  QPen mPen, mSelectedPen;
  QBrush mBrush, mSelectedBrush;
  double mSize;
  TracerStyle mStyle;
  HGraph *mGraph;
  double mGraphKey;
  bool mInterpolating;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);

  // non-virtual methods:
  QPen mainPen() const;
  QBrush mainBrush() const;
};


class H_LIB_DECL HItemBracket : public HAbstractItem
{
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
  Q_PROPERTY(double length READ length WRITE setLength)
  Q_PROPERTY(BracketStyle style READ style WRITE setStyle)
  /// \endcond
public:
  enum BracketStyle { bsSquare  ///< A brace with angled edges
                      ,bsRound  ///< A brace with round edges
                      ,bsCurly  ///< A curly brace
                      ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression
  };

  HItemBracket(HPlot *parentPlot);
  virtual ~HItemBracket();

  // getters:
  QPen pen() const { return mPen; }
  QPen selectedPen() const { return mSelectedPen; }
  double length() const { return mLength; }
  BracketStyle style() const { return mStyle; }

  // setters;
  void setPen(const QPen &pen);
  void setSelectedPen(const QPen &pen);
  void setLength(double length);
  void setStyle(BracketStyle style);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;

  HItemPosition * const left;
  HItemPosition * const right;
  HItemAnchor * const center;

protected:
  // property members:
  enum AnchorIndex {aiCenter};
  QPen mPen, mSelectedPen;
  double mLength;
  BracketStyle mStyle;

  // reimplemented virtual methods:
  virtual void draw(HPainter *painter);
  virtual QPointF anchorPixelPoint(int anchorId) const;

  // non-virtual methods:
  QPen mainPen() const;
};



#endif // HCUSTOMPLOT_H
